@amaster.ai/employee-runtime-connector 0.1.0-beta.5 → 0.1.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/amaster-runtime-daemon.mjs +714 -66
- package/dist/amaster-runtime.mjs +1 -1
- package/package.json +1 -1
|
@@ -2335,7 +2335,7 @@ import { copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSy
|
|
|
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.
|
|
2338
|
+
const CONNECTOR_VERSION = "0.1.0-beta.7";
|
|
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;
|
|
@@ -3527,6 +3527,7 @@ function runtimeActionsInstruction(options = {}) {
|
|
|
3527
3527
|
const artifactVerifierCommands = Array.isArray(options.artifactVerifierCommands)
|
|
3528
3528
|
? options.artifactVerifierCommands.map(readString).filter(Boolean)
|
|
3529
3529
|
: [];
|
|
3530
|
+
const actionValidatorCommand = readString(options.actionValidatorCommand);
|
|
3530
3531
|
return [
|
|
3531
3532
|
"Runtime action bridge:",
|
|
3532
3533
|
"Choose exactly one mutation path for each requested action: direct AMaster API or the runtime action bridge. Never replay the same mutation through both paths.",
|
|
@@ -3537,12 +3538,17 @@ function runtimeActionsInstruction(options = {}) {
|
|
|
3537
3538
|
"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.",
|
|
3538
3539
|
"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.",
|
|
3539
3540
|
"Creating child tasks and delegating them to existing agents is task execution, not organization orchestration. Use assignee ids only from the current task context; server permissions still decide whether an assignment is allowed.",
|
|
3541
|
+
"Runtime actions use a strict schema. Unknown fields, unsupported action types, duplicate clientKey values, unknown client-key references, and client-key dependency cycles reject the entire batch before any mutation. blockedByIssueIds is supported only on create_child_task for existing issue UUIDs supplied by the current task context; connector preflight checks UUID shape, while the server validates same-company existence and permissions. Do not place it on update_parent or invent fields that are not in the canonical action examples.",
|
|
3542
|
+
"Use native JSON scalar types exactly: booleans such as create_child_task.blockParentUntilDone, top-level deliverables[].required, and create_interaction payload questions[].required must be true or false without quotes. Numbers such as minSelected/maxSelected/version must be numeric literals without quotes. String forms such as \"true\" or \"1\" reject the whole batch; AMaster does not silently coerce them.",
|
|
3543
|
+
"For same-batch relationships, assign a unique clientKey to each agent_hire or create_child_task producer. Use reportsToAgentClientKey for a new agent's manager, assigneeAgentClientKey for a child owner, parentClientKey for a nested child, and blockedByClientKeys for first-class child blockers. Use blockedByIssueIds only when a new child depends on existing issue UUIDs supplied by the current task context; connector preflight checks UUID shape and the server validates same-company existence. References may point forward within the same batch; every referenced key must resolve to the correct resource kind in that batch.",
|
|
3540
3544
|
options.explicitDecompositionRequired
|
|
3541
|
-
? "This standard task explicitly requires child task decomposition. The runtime action batch must include create_child_task or one suggest_tasks interaction unless child tasks already exist. A checklist in a comment or artifact is not decomposition. Create independently executable internal preparation tasks now.
|
|
3545
|
+
? "This standard task explicitly requires child task decomposition. The runtime action batch must include create_child_task or one suggest_tasks interaction unless child tasks already exist. A checklist in a comment or artifact is not decomposition. Create independently executable internal preparation tasks now. Use backlog for recorded work that should not start now, and use suggest_tasks for work that must wait for human approval. When a pending interaction is created, keep the parent in_review rather than in_progress or done."
|
|
3542
3546
|
: "",
|
|
3543
|
-
"If child tasks must wait for a human confirmation, use one suggest_tasks interaction so AMaster creates and wakes the selected tasks only after acceptance. A batch that combines executable create_child_task actions with a pending interaction is rejected before mutation unless every such child is backlog or explicitly sets executeBeforeConfirmation true.",
|
|
3544
3547
|
options.canOrchestrateOrganization
|
|
3545
|
-
? "
|
|
3548
|
+
? "Pending-interaction batch gate: before emitting JSON, scan every agent_hire and create_child_task. Every co-batched hire must independently set executeBeforeConfirmation true; every create_child_task must independently be backlog, set executeBeforeConfirmation true, or move into suggest_tasks. blockParentUntilDone=false and a flagged assignee hire do not exempt a child. Otherwise move the hires to a later continuation and use suggest_tasks for work that must wait for acceptance. This applies to every pending interaction, including budget or business approval."
|
|
3549
|
+
: "Pending-interaction batch gate: before emitting JSON, scan every create_child_task. Every create_child_task must independently be backlog, set executeBeforeConfirmation true, or move into suggest_tasks. blockParentUntilDone=false does not exempt a child. Use suggest_tasks for work that must wait for acceptance.",
|
|
3550
|
+
options.canOrchestrateOrganization
|
|
3551
|
+
? "For AMaster organization-building requests, such as creating other roles, hiring digital employees, building the first team, or assigning work to roles, do not stop at a strategy document. Include agent_hire for each needed role and create_child_task for that role's first concrete assignment. agent_hire already creates the native pending hire approval; do not also ask whether to approve that hire in a create_interaction action in the same batch. If approval must happen before any hire record exists, create only a proposal interaction and hire in a later continuation. These organization batches must include add_comment or update_parent.comment explaining what was created, requested, assigned, or blocked."
|
|
3546
3552
|
: "",
|
|
3547
3553
|
options.canOrchestrateOrganization
|
|
3548
3554
|
? `For agent_hire, set adapterType explicitly. Default to the current executing agent adapterType${currentAgentAdapterType ? ` (${currentAgentAdapterType})` : ""} unless the task names a different approved adapter.`
|
|
@@ -3557,16 +3563,26 @@ function runtimeActionsInstruction(options = {}) {
|
|
|
3557
3563
|
"Never use write, bash, cat, printf, echo, or any other tool to emit the marker or JSON. You may prepare and validate the envelope in a temporary workspace file, but the final assistant message itself must contain the exact marker and JSON; tool output is ignored by the runtime action bridge.",
|
|
3558
3564
|
"The runtime action block must be strict JSON parseable by JSON.parse. Do not use ellipses, placeholders such as [...], comments, trailing commas, single quotes, markdown tables outside quoted strings, or raw double quotes inside string values. Escape quotes and newlines in string values, or generate the block with JSON.stringify / @amaster/runtime-sdk before including it in the final assistant message.",
|
|
3559
3565
|
"Before final output, validate the runtime action block with JSON.parse. If it would fail, fix the JSON and only then include AMASTER_RUNTIME_ACTIONS in the final assistant message.",
|
|
3566
|
+
actionValidatorCommand
|
|
3567
|
+
? `Before final output, write only the JSON envelope to .amaster-runtime-actions.json and run this exact strict-schema validator: ${actionValidatorCommand}. Fix every reported error and rerun until it exits 0. JSON.parse alone is not sufficient. Do not include the marker in the temporary file.`
|
|
3568
|
+
: "",
|
|
3560
3569
|
`Allowed action types: ${allowedActionTypes}.`,
|
|
3561
|
-
"For every create_interaction action, set top-level kind to the interaction kind and set payload to a JSON object. Use this complete canonical request_confirmation example: {\"type\":\"create_interaction\",\"kind\":\"request_confirmation\",\"payload\":{\"version\":1,\"prompt\":\"Confirm this result?\",\"acceptLabel\":\"确认\",\"rejectLabel\":\"调整\"}}. Never use interactionType.",
|
|
3562
|
-
"For
|
|
3570
|
+
"For every create_interaction action, set top-level kind to the interaction kind and set payload to a JSON object. Use this complete canonical request_confirmation example: {\"type\":\"create_interaction\",\"kind\":\"request_confirmation\",\"continuationPolicy\":\"wake_assignee_on_accept\",\"payload\":{\"version\":1,\"prompt\":\"Confirm this result?\",\"acceptLabel\":\"确认\",\"rejectLabel\":\"调整\"}}. Never use interactionType.",
|
|
3571
|
+
"Interactions cannot be updated with PATCH after creation. For direct API calls, include continuationPolicy in the initial POST /api/issues/{issueId}/interactions request and use only the documented POST accept, reject, respond, or cancel resolution endpoint afterward.",
|
|
3572
|
+
"For create_interaction ask_user_questions, use continuationPolicy wake_assignee, never wake_assignee_on_accept, because answering questions does not produce accepted status. Every question requires 1-10 options; do not emit an open-text question without options. The UI provides an Other text answer alongside the declared options. Use payload {\"version\":1,\"title\":\"...\",\"questions\":[{\"id\":\"question_key\",\"prompt\":\"Question text\",\"selectionMode\":\"single\",\"required\":true,\"options\":[{\"id\":\"option_key\",\"label\":\"Option label\"}]}]}. Do not use question/options.value at the top level.",
|
|
3563
3573
|
"For create_interaction request_confirmation, use payload {\"version\":1,\"prompt\":\"Confirm this result?\",\"acceptLabel\":\"确认\",\"rejectLabel\":\"调整\"}. Do not use options arrays for request_confirmation.",
|
|
3564
3574
|
"For create_interaction request_checkbox_confirmation, use payload {\"version\":1,\"prompt\":\"Select approved items\",\"options\":[{\"id\":\"option_key\",\"label\":\"Option label\"}],\"minSelected\":1,\"maxSelected\":2,\"acceptLabel\":\"确认\",\"rejectLabel\":\"调整\"}.",
|
|
3565
3575
|
"For create_interaction suggest_tasks, use payload {\"version\":1,\"tasks\":[{\"clientKey\":\"task_key\",\"title\":\"Task title\",\"description\":\"Task description\",\"priority\":\"medium\"}]}.",
|
|
3566
3576
|
"When a suggested task should start automatically after acceptance, set assigneeAgentId to an agent id from the current task context. Set assigneeUserId only when a known user must own the task. Omit both assignee fields only when the accepted task should remain intentionally unassigned in backlog. Never substitute a role name for an assignee id.",
|
|
3577
|
+
"An external input or human approval cannot exist only as free-text owner/next-step prose. Create a human-owned create_child_task with assigneeUserId and blockParentUntilDone=true (or another explicit interaction/dependency path), then keep the parent blocked or in_review until it resolves.",
|
|
3567
3578
|
"Do not copy placeholder names, placeholder summaries, placeholder action arrays, or placeholder paths. Every title, body, role, idempotencyKey, artifact path, and task description must be specific to the current issue and company.",
|
|
3568
3579
|
"Only include upload_artifact for a file that you actually created in the execution workspace and whose path exists relative to cwd. If no file was created, omit upload_artifact.",
|
|
3580
|
+
"upload_artifact actions do not accept required. Artifact required belongs only to top-level deliverables entries, not action objects.",
|
|
3581
|
+
"When a child artifact is promoted as a replacement for a parent artifact, run upload_artifact on the parent issue and include both sourceWorkProductId (the child artifact) and supersedesWorkProductId (the current parent artifact). Never claim the parent artifact was corrected without this durable lineage.",
|
|
3582
|
+
"For derived artifacts, set evidenceRole=derived, sourceWorkProductIds to every consumed input work product, and scenarioKeys to the unchanged source scenario set. For post-implementation review artifacts, set evidenceRole=review and sourceWorkProductIds to the exact current artifacts reviewed; stale or missing sources fail closed.",
|
|
3583
|
+
"When creating a review child that must run after sibling implementation or governance tasks, express that ordering with blockedByClientKeys in the same create_child_task batch. Do not start a post-implementation review as an independent sibling without dependencies.",
|
|
3569
3584
|
"For required deliverables, add top-level deliverables entries using {\"path\":\"relative/file\",\"required\":true}.",
|
|
3585
|
+
"update_parent accepts only status, comment, and force. Never put deliverables, artifacts, interactions, blockers, or child-task fields inside update_parent; keep top-level deliverables beside actions and use their dedicated action objects.",
|
|
3570
3586
|
artifactVerifierCommands.length > 0
|
|
3571
3587
|
? `Executable deliverable verification is enabled. Allowed verifier commands: ${artifactVerifierCommands.join(", ")}. For runnable outputs, add verification {\"command\":\"${artifactVerifierCommands[0]}\",\"args\":[\"script.js\"],\"timeoutSeconds\":30,\"expectedExitCode\":0,\"expectedOutputs\":[\"result.json\"]}. Commands are argv-only and run without a shell.`
|
|
3572
3588
|
: "Executable deliverable verification is disabled by runtime policy. Do not include verification blocks. Required deliverables are still checked for presence and upload completeness.",
|
|
@@ -3860,11 +3876,17 @@ function buildCommandPrompt(command, workspace, materializedAttachments = [], op
|
|
|
3860
3876
|
&& runtimeActionChildIssueSummaryEntries(command).length === 0;
|
|
3861
3877
|
const executingAgent = asRecord(context.paperclipExecutingAgent);
|
|
3862
3878
|
const currentAgentAdapterType = readString(executingAgent.adapterType);
|
|
3879
|
+
const validatorContentTypes = runtimeAttachmentContentTypes(command);
|
|
3880
|
+
// Built-in executors run on the connector host, so the prompt may reference these host paths.
|
|
3881
|
+
// A custom wrapper may isolate model tools; until isolation is an explicit capability, this
|
|
3882
|
+
// validator is same-host only and must fail visibly rather than fall back to a weaker check.
|
|
3883
|
+
const actionValidatorCommand = `${quoteShell(process.execPath)} ${quoteShell(resolve(process.argv[1]))} validate-actions --file .amaster-runtime-actions.json --cwd . --allowed-content-types ${quoteShell(validatorContentTypes.join(","))}`;
|
|
3863
3884
|
return [
|
|
3864
3885
|
"## AMaster Runtime Connector Task",
|
|
3865
3886
|
"",
|
|
3866
3887
|
"You are executing a task dispatched by AMaster Employee from the central control plane.",
|
|
3867
3888
|
"Work only inside the declared workspace. Make concrete progress and finish with a concise result summary.",
|
|
3889
|
+
"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.",
|
|
3868
3890
|
"",
|
|
3869
3891
|
`- command id: ${command.commandId}`,
|
|
3870
3892
|
`- run id: ${commandRunId(command) ?? "unknown"}`,
|
|
@@ -3872,7 +3894,9 @@ function buildCommandPrompt(command, workspace, materializedAttachments = [], op
|
|
|
3872
3894
|
issueLine ? `- issue: ${issueLine}` : "",
|
|
3873
3895
|
`- workspace: ${cwd}`,
|
|
3874
3896
|
workspaceContext.managed ? `- source workspace: ${workspaceContext.sourceWorkspacePath}` : "",
|
|
3875
|
-
workspaceContext.managed
|
|
3897
|
+
workspaceContext.managed
|
|
3898
|
+
? "Use the execution workspace as cwd. Never recursively scan the source workspace, the user's home directory, or any path outside the execution workspace. Only inspect a specific external source path when the task explicitly requires that exact path."
|
|
3899
|
+
: "",
|
|
3876
3900
|
readString(context.wakeReason) ? `- wake reason: ${readString(context.wakeReason)}` : "",
|
|
3877
3901
|
hasRuntimeApi
|
|
3878
3902
|
? "- AMaster API env: PAPERCLIP_API_URL, PAPERCLIP_API_KEY, AMASTER_BOARD_API_KEY, PAPERCLIP_RUN_ID, PAPERCLIP_TASK_ID, PAPERCLIP_AGENT_ID, PAPERCLIP_COMPANY_ID are available to update the task, create child tasks, and report progress. Treat API keys as secrets; never print them or include them in runtime actions."
|
|
@@ -3883,6 +3907,7 @@ function buildCommandPrompt(command, workspace, materializedAttachments = [], op
|
|
|
3883
3907
|
currentAgentAdapterType,
|
|
3884
3908
|
explicitDecompositionRequired,
|
|
3885
3909
|
artifactVerifierCommands: options.artifactVerifierCommands,
|
|
3910
|
+
actionValidatorCommand,
|
|
3886
3911
|
}) : "",
|
|
3887
3912
|
"",
|
|
3888
3913
|
agentInstructions,
|
|
@@ -5358,6 +5383,15 @@ function parseRuntimeActionEnvelopeJson(jsonText) {
|
|
|
5358
5383
|
}
|
|
5359
5384
|
}
|
|
5360
5385
|
|
|
5386
|
+
function hasMalformedRuntimeActionMarkerIntent(text) {
|
|
5387
|
+
const value = String(text ?? "");
|
|
5388
|
+
if (!value.includes("AMASTER_RUNTIME_ACTIONS")) return false;
|
|
5389
|
+
const hasNonCanonicalMarkerLine = value
|
|
5390
|
+
.split(/\r?\n/)
|
|
5391
|
+
.some((line) => line.includes("AMASTER_RUNTIME_ACTIONS") && line.trim() !== "AMASTER_RUNTIME_ACTIONS");
|
|
5392
|
+
return hasNonCanonicalMarkerLine && /["']actions["']\s*:/.test(value);
|
|
5393
|
+
}
|
|
5394
|
+
|
|
5361
5395
|
function readBoundedActionString(record, key, maxChars) {
|
|
5362
5396
|
const value = readString(record[key]);
|
|
5363
5397
|
return value ? truncateText(value, maxChars) : null;
|
|
@@ -6021,6 +6055,9 @@ function runtimeChildActionFingerprint(action, issueId, parentIssueId) {
|
|
|
6021
6055
|
status: readActionStatus(action.status) ?? null,
|
|
6022
6056
|
priority: readActionPriority(action.priority) ?? null,
|
|
6023
6057
|
parentClientKey: readString(action.parentClientKey) ?? null,
|
|
6058
|
+
blockedByClientKeys: Array.isArray(action.blockedByClientKeys)
|
|
6059
|
+
? action.blockedByClientKeys.map(readString).filter(Boolean).sort()
|
|
6060
|
+
: [],
|
|
6024
6061
|
})}`;
|
|
6025
6062
|
}
|
|
6026
6063
|
|
|
@@ -6038,6 +6075,7 @@ function runtimeAgentHireActionFingerprint(action, companyId, issueId, body) {
|
|
|
6038
6075
|
adapterType: readString(body.adapterType) ?? null,
|
|
6039
6076
|
sourceIssueId: readString(body.sourceIssueId) ?? null,
|
|
6040
6077
|
sourceIssueIds: runtimeActionIssueIdList(body.sourceIssueIds).sort(),
|
|
6078
|
+
reportsToAgentClientKey: readString(action.reportsToAgentClientKey) ?? null,
|
|
6041
6079
|
})}`;
|
|
6042
6080
|
}
|
|
6043
6081
|
|
|
@@ -6085,21 +6123,6 @@ function runtimeActionDoneNeedsExternalHuman(action, actionContext) {
|
|
|
6085
6123
|
].some((pattern) => pattern.test(normalized));
|
|
6086
6124
|
}
|
|
6087
6125
|
|
|
6088
|
-
function runtimeActionExternalHumanPatch(comment) {
|
|
6089
|
-
const owner = "人工负责人";
|
|
6090
|
-
const action = "补充或授权外部输入后再继续;涉及个人信息、付款、报名提交或审批时由人工处理";
|
|
6091
|
-
const guard = [
|
|
6092
|
-
"AMaster 已检测到本次运行仍依赖人工授权或外部输入,任务已转为 blocked / external_owner_action。本次运行不会自动发送飞书消息;请通过人工协作或已配置的飞书 CLI 通知对应负责人处理,收到明确授权后再继续。",
|
|
6093
|
-
"",
|
|
6094
|
-
`external owner: ${owner}`,
|
|
6095
|
-
`external action: ${action}`,
|
|
6096
|
-
].join("\n");
|
|
6097
|
-
return {
|
|
6098
|
-
status: "blocked",
|
|
6099
|
-
comment: comment ? `${comment}\n\n${guard}` : guard,
|
|
6100
|
-
};
|
|
6101
|
-
}
|
|
6102
|
-
|
|
6103
6126
|
function runtimeActionReviewConfirmation(action, runtimeAuth, command, actionContext, opts = {}) {
|
|
6104
6127
|
const runId = runtimeActionRunId(runtimeAuth) ?? commandRunId(command) ?? "unknown-run";
|
|
6105
6128
|
const comment = readBoundedActionString(action, "comment", 20_000)
|
|
@@ -6249,6 +6272,24 @@ function runtimeActionContinuationScope(command) {
|
|
|
6249
6272
|
return ["revision_only", "interaction_accepted", "message_only"].includes(scope ?? "") ? scope : null;
|
|
6250
6273
|
}
|
|
6251
6274
|
|
|
6275
|
+
function runtimeActionAuthorizationEnvelope(command) {
|
|
6276
|
+
const payload = asRecord(command.payload);
|
|
6277
|
+
const context = asRecord(payload.contextSnapshot);
|
|
6278
|
+
const rawEnvelope = asRecord(context.authorizationEnvelope);
|
|
6279
|
+
const allowedActionClasses = Array.isArray(rawEnvelope.allowedActionClasses)
|
|
6280
|
+
? rawEnvelope.allowedActionClasses.map(readString).filter(Boolean)
|
|
6281
|
+
: [];
|
|
6282
|
+
const effectiveAllowedActionClasses = allowedActionClasses.length > 0
|
|
6283
|
+
? allowedActionClasses
|
|
6284
|
+
: ["task_governance"];
|
|
6285
|
+
return {
|
|
6286
|
+
allowedActionClasses: new Set(effectiveAllowedActionClasses),
|
|
6287
|
+
sourceKind: readString(rawEnvelope.sourceKind) ?? "implicit_default",
|
|
6288
|
+
sourceId: readString(rawEnvelope.sourceId) ?? commandRunId(command),
|
|
6289
|
+
sourceRevision: readString(rawEnvelope.sourceRevision) ?? "missing_envelope",
|
|
6290
|
+
};
|
|
6291
|
+
}
|
|
6292
|
+
|
|
6252
6293
|
function runtimeActionInReviewNeedsFollowUpText(text) {
|
|
6253
6294
|
const normalized = String(text ?? "")
|
|
6254
6295
|
.replace(/\s+/g, "")
|
|
@@ -6411,6 +6452,35 @@ function runtimeActionParentIssueId(action, actionContext, fallbackIssueId) {
|
|
|
6411
6452
|
return parentIssueId;
|
|
6412
6453
|
}
|
|
6413
6454
|
|
|
6455
|
+
function runtimeActionReportsToAgentId(action, actionContext) {
|
|
6456
|
+
const reportsToAgentClientKey = readString(action.reportsToAgentClientKey);
|
|
6457
|
+
if (!reportsToAgentClientKey) return readString(action.reportsTo);
|
|
6458
|
+
const reportsTo = actionContext.agentIdByClientKey.get(reportsToAgentClientKey);
|
|
6459
|
+
if (!reportsTo) {
|
|
6460
|
+
throw new Error(`agent_hire action references unknown reportsToAgentClientKey: ${reportsToAgentClientKey}`);
|
|
6461
|
+
}
|
|
6462
|
+
return reportsTo;
|
|
6463
|
+
}
|
|
6464
|
+
|
|
6465
|
+
function runtimeActionBlockedByIssueIds(action, actionContext) {
|
|
6466
|
+
const explicitIssueIds = Array.isArray(action.blockedByIssueIds)
|
|
6467
|
+
? action.blockedByIssueIds.map((rawIssueId) => readString(rawIssueId)).filter(Boolean)
|
|
6468
|
+
: [];
|
|
6469
|
+
if (action.blockedByClientKeys === undefined) return [...new Set(explicitIssueIds)];
|
|
6470
|
+
const clientKeyIssueIds = action.blockedByClientKeys.map((rawClientKey, index) => {
|
|
6471
|
+
const clientKey = readString(rawClientKey);
|
|
6472
|
+
if (!clientKey) {
|
|
6473
|
+
throw runtimeActionValidationError(`create_child_task blockedByClientKeys[${index}] must be a non-empty string`);
|
|
6474
|
+
}
|
|
6475
|
+
const blockerIssueId = actionContext.issueIdByClientKey.get(clientKey);
|
|
6476
|
+
if (!blockerIssueId) {
|
|
6477
|
+
throw new Error(`create_child_task action references unknown blockedByClientKeys entry: ${clientKey}`);
|
|
6478
|
+
}
|
|
6479
|
+
return blockerIssueId;
|
|
6480
|
+
});
|
|
6481
|
+
return [...new Set([...explicitIssueIds, ...clientKeyIssueIds])];
|
|
6482
|
+
}
|
|
6483
|
+
|
|
6414
6484
|
const RUNTIME_EXECUTOR_ADAPTER_TYPE_ALIASES = new Map([
|
|
6415
6485
|
["pi", "pi_local"],
|
|
6416
6486
|
["pi_local", "pi_local"],
|
|
@@ -6858,6 +6928,237 @@ function assertRuntimeActionValid(condition, message) {
|
|
|
6858
6928
|
if (!condition) throw runtimeActionValidationError(message);
|
|
6859
6929
|
}
|
|
6860
6930
|
|
|
6931
|
+
const RUNTIME_ACTION_COMMON_FIELDS = new Set(["type", "action", "name", "payload"]);
|
|
6932
|
+
const RUNTIME_ACTION_ALLOWED_FIELDS = new Map([
|
|
6933
|
+
["create_child_task", new Set([
|
|
6934
|
+
"title", "description", "status", "priority", "assigneeAgentId", "assigneeUserId",
|
|
6935
|
+
"assigneeAgentClientKey", "agentClientKey", "blockParentUntilDone", "executeBeforeConfirmation",
|
|
6936
|
+
"clientKey", "idempotencyKey", "parentClientKey", "blockedByIssueIds", "blockedByClientKeys", "create_child_task",
|
|
6937
|
+
])],
|
|
6938
|
+
["agent_hire", new Set([
|
|
6939
|
+
"clientKey", "idempotencyKey", "key", "companyId", "role", "title", "icon", "reportsTo",
|
|
6940
|
+
"reportsToAgentClientKey", "capabilities", "desiredSkills", "adapterType", "adapterConfig",
|
|
6941
|
+
"instructionsBundle", "runtimeConfig", "defaultEnvironmentId", "budgetMonthlyCents", "permissions",
|
|
6942
|
+
"metadata", "sourceIssueId", "sourceIssueIds", "executeBeforeConfirmation", "agent_hire", "agent_hire/create_agent",
|
|
6943
|
+
])],
|
|
6944
|
+
["create_agent", new Set([
|
|
6945
|
+
"clientKey", "idempotencyKey", "key", "companyId", "role", "title", "icon", "reportsTo",
|
|
6946
|
+
"reportsToAgentClientKey", "capabilities", "desiredSkills", "adapterType", "adapterConfig",
|
|
6947
|
+
"instructionsBundle", "runtimeConfig", "defaultEnvironmentId", "budgetMonthlyCents", "permissions",
|
|
6948
|
+
"metadata", "sourceIssueId", "sourceIssueIds", "executeBeforeConfirmation", "create_agent",
|
|
6949
|
+
])],
|
|
6950
|
+
["add_comment", new Set(["body", "content", "text", "message", "add_comment"])],
|
|
6951
|
+
["upload_artifact", new Set([
|
|
6952
|
+
"path", "filePath", "artifactPath", "artifact_path", "title", "summary", "contentType",
|
|
6953
|
+
"createWorkProduct", "provider", "status", "reviewState", "isPrimary", "healthStatus",
|
|
6954
|
+
"sourceWorkProductId", "sourceWorkProductIds", "supersedesWorkProductId", "evidenceRole", "scenarioKeys",
|
|
6955
|
+
"idempotencyKey", "upload_artifact",
|
|
6956
|
+
])],
|
|
6957
|
+
["create_interaction", new Set([
|
|
6958
|
+
"kind", "continuationPolicy", "title", "summary", "idempotencyKey", "interaction", "version",
|
|
6959
|
+
"prompt", "question", "body", "content", "text", "message", "description", "acceptLabel",
|
|
6960
|
+
"requestChangesLabel", "requestChangesReasonLabel", "rejectLabel", "rejectRequiresReason",
|
|
6961
|
+
"rejectReasonLabel", "allowDeclineReason", "declineReasonPlaceholder", "detailsMarkdown",
|
|
6962
|
+
"supersedeOnUserComment", "target", "artifactRefs", "tasks", "options", "defaultSelectedOptionIds",
|
|
6963
|
+
"minSelected", "maxSelected", "questions", "submitLabel", "selectionMode", "required", "helpText",
|
|
6964
|
+
"create_interaction",
|
|
6965
|
+
])],
|
|
6966
|
+
["update_parent", new Set(["status", "comment", "force", "update_parent"])],
|
|
6967
|
+
["update_company_profile", new Set([
|
|
6968
|
+
"companyId", "tagline", "stage", "targetCustomers", "products", "currentPriority", "businessModel",
|
|
6969
|
+
"regions", "brandTone", "markAutoDerived", "idempotencyKey", "update_company_profile",
|
|
6970
|
+
])],
|
|
6971
|
+
["create_routine", new Set([
|
|
6972
|
+
"companyId", "title", "description", "cronExpression", "priority", "concurrencyPolicy", "catchUpPolicy",
|
|
6973
|
+
"assigneeAgentId", "timezone", "idempotencyKey", "create_routine",
|
|
6974
|
+
])],
|
|
6975
|
+
]);
|
|
6976
|
+
|
|
6977
|
+
function runtimeActionReceivedType(value) {
|
|
6978
|
+
if (value === null) return "null";
|
|
6979
|
+
if (Array.isArray(value)) return "array";
|
|
6980
|
+
return typeof value;
|
|
6981
|
+
}
|
|
6982
|
+
|
|
6983
|
+
function assertNoUnsupportedRuntimeActionFields(action, actionIndex) {
|
|
6984
|
+
const type = readRuntimeActionType(action);
|
|
6985
|
+
const allowed = RUNTIME_ACTION_ALLOWED_FIELDS.get(type);
|
|
6986
|
+
assertRuntimeActionValid(allowed, `unsupported AMaster runtime action type: ${type ?? "missing"}`);
|
|
6987
|
+
const fields = runtimeActionBusinessFields(action);
|
|
6988
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
6989
|
+
if (type === "create_interaction" && field === "interactionType") continue;
|
|
6990
|
+
if (RUNTIME_ACTION_COMMON_FIELDS.has(field) || allowed.has(field)) continue;
|
|
6991
|
+
const fieldPath = `actions[${actionIndex}].${field}`;
|
|
6992
|
+
const receivedType = runtimeActionReceivedType(value);
|
|
6993
|
+
const err = runtimeActionValidationError(
|
|
6994
|
+
`${type} action has unsupported field ${field} at ${fieldPath}; received ${receivedType}`,
|
|
6995
|
+
);
|
|
6996
|
+
err.runtimeActionFieldPath = fieldPath;
|
|
6997
|
+
err.runtimeActionReceivedType = receivedType;
|
|
6998
|
+
throw err;
|
|
6999
|
+
}
|
|
7000
|
+
}
|
|
7001
|
+
|
|
7002
|
+
const RUNTIME_ACTION_BOOLEAN_FIELDS = new Set([
|
|
7003
|
+
"blockParentUntilDone", "executeBeforeConfirmation", "createWorkProduct", "isPrimary",
|
|
7004
|
+
"force", "markAutoDerived", "rejectRequiresReason", "allowDeclineReason", "supersedeOnUserComment",
|
|
7005
|
+
"required",
|
|
7006
|
+
]);
|
|
7007
|
+
const RUNTIME_ACTION_NUMBER_FIELDS = new Set(["budgetMonthlyCents", "minSelected", "maxSelected", "version"]);
|
|
7008
|
+
const RUNTIME_ACTION_ARRAY_FIELDS = new Set([
|
|
7009
|
+
"blockedByIssueIds", "blockedByClientKeys", "desiredSkills", "sourceIssueIds", "artifactRefs", "tasks",
|
|
7010
|
+
"options", "defaultSelectedOptionIds", "questions", "sourceWorkProductIds", "scenarioKeys",
|
|
7011
|
+
]);
|
|
7012
|
+
const RUNTIME_ACTION_OBJECT_FIELDS = new Set([
|
|
7013
|
+
"adapterConfig", "instructionsBundle", "runtimeConfig", "permissions", "metadata", "target",
|
|
7014
|
+
"interaction",
|
|
7015
|
+
]);
|
|
7016
|
+
|
|
7017
|
+
function assertRuntimeActionFieldTypes(action, actionIndex) {
|
|
7018
|
+
const fields = runtimeActionBusinessFields(action);
|
|
7019
|
+
const type = readRuntimeActionType(fields) ?? "unknown";
|
|
7020
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
7021
|
+
if (value === undefined) continue;
|
|
7022
|
+
if (SINGLE_KEY_RUNTIME_ACTION_TYPES.has(field)) continue;
|
|
7023
|
+
if (type === "create_interaction" && field === "payload") continue;
|
|
7024
|
+
let expectedType = null;
|
|
7025
|
+
let valid = true;
|
|
7026
|
+
if (RUNTIME_ACTION_BOOLEAN_FIELDS.has(field)) {
|
|
7027
|
+
expectedType = "boolean";
|
|
7028
|
+
valid = typeof value === "boolean";
|
|
7029
|
+
} else if (RUNTIME_ACTION_NUMBER_FIELDS.has(field)) {
|
|
7030
|
+
expectedType = "number";
|
|
7031
|
+
valid = typeof value === "number" && Number.isFinite(value);
|
|
7032
|
+
} else if (RUNTIME_ACTION_ARRAY_FIELDS.has(field)) {
|
|
7033
|
+
expectedType = "array";
|
|
7034
|
+
valid = Array.isArray(value);
|
|
7035
|
+
} else if (field === "capabilities") {
|
|
7036
|
+
expectedType = "string or array";
|
|
7037
|
+
valid = typeof value === "string" || Array.isArray(value);
|
|
7038
|
+
} else if (RUNTIME_ACTION_OBJECT_FIELDS.has(field) || field === "payload") {
|
|
7039
|
+
expectedType = "object";
|
|
7040
|
+
valid = value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7041
|
+
} else if (!RUNTIME_ACTION_COMMON_FIELDS.has(field) && value !== null) {
|
|
7042
|
+
expectedType = "string";
|
|
7043
|
+
valid = typeof value === "string";
|
|
7044
|
+
}
|
|
7045
|
+
if (valid || !expectedType) continue;
|
|
7046
|
+
const fieldPath = `actions[${actionIndex}].${field}`;
|
|
7047
|
+
const err = runtimeActionValidationError(
|
|
7048
|
+
`${type} action field ${fieldPath} must be ${expectedType}; received ${runtimeActionReceivedType(value)}`,
|
|
7049
|
+
);
|
|
7050
|
+
err.runtimeActionFieldPath = fieldPath;
|
|
7051
|
+
err.runtimeActionReceivedType = runtimeActionReceivedType(value);
|
|
7052
|
+
throw err;
|
|
7053
|
+
}
|
|
7054
|
+
}
|
|
7055
|
+
|
|
7056
|
+
function runtimeActionClientKeyProducerKind(type) {
|
|
7057
|
+
if (["agent_hire", "create_agent"].includes(type ?? "")) return "agent";
|
|
7058
|
+
if (type === "create_child_task") return "issue";
|
|
7059
|
+
return null;
|
|
7060
|
+
}
|
|
7061
|
+
|
|
7062
|
+
function runtimeActionClientKeyReferences(fields) {
|
|
7063
|
+
const type = readRuntimeActionType(fields);
|
|
7064
|
+
const references = [];
|
|
7065
|
+
if (["agent_hire", "create_agent"].includes(type ?? "")) {
|
|
7066
|
+
const clientKey = readString(fields.reportsToAgentClientKey);
|
|
7067
|
+
if (clientKey) references.push({ clientKey, expectedKind: "agent", field: "reportsToAgentClientKey" });
|
|
7068
|
+
}
|
|
7069
|
+
if (type === "create_child_task") {
|
|
7070
|
+
const assigneeClientKey = readString(fields.assigneeAgentClientKey) ?? readString(fields.agentClientKey);
|
|
7071
|
+
if (assigneeClientKey) references.push({ clientKey: assigneeClientKey, expectedKind: "agent", field: "assigneeAgentClientKey" });
|
|
7072
|
+
const parentClientKey = readString(fields.parentClientKey);
|
|
7073
|
+
if (parentClientKey) references.push({ clientKey: parentClientKey, expectedKind: "issue", field: "parentClientKey" });
|
|
7074
|
+
if (fields.blockedByClientKeys !== undefined) {
|
|
7075
|
+
assertRuntimeActionValid(Array.isArray(fields.blockedByClientKeys), "create_child_task blockedByClientKeys must be an array");
|
|
7076
|
+
for (const [index, value] of fields.blockedByClientKeys.entries()) {
|
|
7077
|
+
const clientKey = readString(value);
|
|
7078
|
+
assertRuntimeActionValid(clientKey, `create_child_task blockedByClientKeys[${index}] must be a non-empty string`);
|
|
7079
|
+
references.push({ clientKey, expectedKind: "issue", field: `blockedByClientKeys[${index}]` });
|
|
7080
|
+
}
|
|
7081
|
+
}
|
|
7082
|
+
}
|
|
7083
|
+
return references;
|
|
7084
|
+
}
|
|
7085
|
+
|
|
7086
|
+
function throwRuntimeActionGraphValidation(message, action, actionIndex, fieldPath) {
|
|
7087
|
+
const err = runtimeActionValidationError(message);
|
|
7088
|
+
err.runtimeActionType = readRuntimeActionType(action) ?? "unknown";
|
|
7089
|
+
err.runtimeActionOriginalType = readRuntimeActionOriginalType(action);
|
|
7090
|
+
err.runtimeActionFieldPath = fieldPath ?? `actions[${actionIndex}]`;
|
|
7091
|
+
throw err;
|
|
7092
|
+
}
|
|
7093
|
+
|
|
7094
|
+
function orderRuntimeActionsByClientKeyDependencies(actions) {
|
|
7095
|
+
const producers = new Map();
|
|
7096
|
+
for (const [index, action] of actions.entries()) {
|
|
7097
|
+
const fields = runtimeActionBusinessFields(action);
|
|
7098
|
+
const type = readRuntimeActionType(fields);
|
|
7099
|
+
const kind = runtimeActionClientKeyProducerKind(type);
|
|
7100
|
+
const clientKey = readString(fields.clientKey);
|
|
7101
|
+
if (!clientKey || !kind) continue;
|
|
7102
|
+
if (producers.has(clientKey)) {
|
|
7103
|
+
throwRuntimeActionGraphValidation(
|
|
7104
|
+
`runtime action clientKey must be unique: ${clientKey}`,
|
|
7105
|
+
action,
|
|
7106
|
+
index,
|
|
7107
|
+
`actions[${index}].clientKey`,
|
|
7108
|
+
);
|
|
7109
|
+
}
|
|
7110
|
+
producers.set(clientKey, { index, kind, type });
|
|
7111
|
+
}
|
|
7112
|
+
|
|
7113
|
+
const dependenciesByIndex = actions.map(() => new Set());
|
|
7114
|
+
const dependentsByIndex = actions.map(() => new Set());
|
|
7115
|
+
for (const [index, action] of actions.entries()) {
|
|
7116
|
+
const fields = runtimeActionBusinessFields(action);
|
|
7117
|
+
for (const reference of runtimeActionClientKeyReferences(fields)) {
|
|
7118
|
+
const producer = producers.get(reference.clientKey);
|
|
7119
|
+
if (!producer) {
|
|
7120
|
+
throwRuntimeActionGraphValidation(
|
|
7121
|
+
`${readRuntimeActionType(fields)} action references unknown ${reference.field}: ${reference.clientKey}`,
|
|
7122
|
+
action,
|
|
7123
|
+
index,
|
|
7124
|
+
`actions[${index}].${reference.field}`,
|
|
7125
|
+
);
|
|
7126
|
+
}
|
|
7127
|
+
if (producer.kind !== reference.expectedKind) {
|
|
7128
|
+
throwRuntimeActionGraphValidation(
|
|
7129
|
+
`${readRuntimeActionType(fields)} action ${reference.field} must reference a ${reference.expectedKind} clientKey: ${reference.clientKey}`,
|
|
7130
|
+
action,
|
|
7131
|
+
index,
|
|
7132
|
+
`actions[${index}].${reference.field}`,
|
|
7133
|
+
);
|
|
7134
|
+
}
|
|
7135
|
+
dependenciesByIndex[index].add(producer.index);
|
|
7136
|
+
dependentsByIndex[producer.index].add(index);
|
|
7137
|
+
}
|
|
7138
|
+
}
|
|
7139
|
+
|
|
7140
|
+
const ready = actions.map((_, index) => index).filter((index) => dependenciesByIndex[index].size === 0);
|
|
7141
|
+
const orderedIndexes = [];
|
|
7142
|
+
while (ready.length > 0) {
|
|
7143
|
+
ready.sort((left, right) => left - right);
|
|
7144
|
+
const index = ready.shift();
|
|
7145
|
+
orderedIndexes.push(index);
|
|
7146
|
+
for (const dependentIndex of dependentsByIndex[index]) {
|
|
7147
|
+
dependenciesByIndex[dependentIndex].delete(index);
|
|
7148
|
+
if (dependenciesByIndex[dependentIndex].size === 0) ready.push(dependentIndex);
|
|
7149
|
+
}
|
|
7150
|
+
}
|
|
7151
|
+
if (orderedIndexes.length !== actions.length) {
|
|
7152
|
+
const cycleIndex = dependenciesByIndex.findIndex((dependencies) => dependencies.size > 0);
|
|
7153
|
+
throwRuntimeActionGraphValidation(
|
|
7154
|
+
"runtime action clientKey dependency graph contains a cycle",
|
|
7155
|
+
actions[cycleIndex],
|
|
7156
|
+
cycleIndex,
|
|
7157
|
+
);
|
|
7158
|
+
}
|
|
7159
|
+
return orderedIndexes.map((index) => actions[index]);
|
|
7160
|
+
}
|
|
7161
|
+
|
|
6861
7162
|
function assertRuntimeInteractionOptions(options, path) {
|
|
6862
7163
|
assertRuntimeActionValid(Array.isArray(options) && options.length > 0 && options.length <= 10, `${path} requires 1-10 options`);
|
|
6863
7164
|
const ids = new Set();
|
|
@@ -6935,6 +7236,16 @@ function willRuntimeActionCreateRootBlockingChild(action) {
|
|
|
6935
7236
|
return fields.blockParentUntilDone === true || childStatus === "blocked";
|
|
6936
7237
|
}
|
|
6937
7238
|
|
|
7239
|
+
function willRuntimeActionCreateHumanOwnedInputPath(action) {
|
|
7240
|
+
const fields = runtimeActionBusinessFields(action);
|
|
7241
|
+
if (readRuntimeActionType(fields) !== "create_child_task") return false;
|
|
7242
|
+
const childStatus = readActionStatus(fields.status) ?? "todo";
|
|
7243
|
+
return Boolean(
|
|
7244
|
+
readString(fields.assigneeUserId) &&
|
|
7245
|
+
(fields.blockParentUntilDone === true || childStatus === "blocked"),
|
|
7246
|
+
);
|
|
7247
|
+
}
|
|
7248
|
+
|
|
6938
7249
|
function willRuntimeActionCreatePendingInteraction(action) {
|
|
6939
7250
|
const fields = runtimeActionBusinessFields(action);
|
|
6940
7251
|
if (readRuntimeActionType(fields) !== "create_interaction") return false;
|
|
@@ -6950,8 +7261,22 @@ function runtimeActionIsAmbiguousApprovalGatedChild(action) {
|
|
|
6950
7261
|
return childStatus !== "backlog";
|
|
6951
7262
|
}
|
|
6952
7263
|
|
|
6953
|
-
function
|
|
7264
|
+
function assertNoAmbiguousApprovalGatedSideEffects(actions) {
|
|
6954
7265
|
if (!actions.some(willRuntimeActionCreatePendingInteraction)) return;
|
|
7266
|
+
const ambiguousHire = actions.find((action) => {
|
|
7267
|
+
const fields = runtimeActionBusinessFields(action);
|
|
7268
|
+
return ["agent_hire", "create_agent"].includes(readRuntimeActionType(fields) ?? "") &&
|
|
7269
|
+
fields.executeBeforeConfirmation !== true;
|
|
7270
|
+
});
|
|
7271
|
+
if (ambiguousHire) {
|
|
7272
|
+
const error = runtimeActionValidationError(
|
|
7273
|
+
"approval-gated agent hire actions require the native hire approval only, a separate proposal interaction, or executeBeforeConfirmation true",
|
|
7274
|
+
);
|
|
7275
|
+
error.runtimeActionType = readRuntimeActionType(ambiguousHire) ?? "agent_hire";
|
|
7276
|
+
error.runtimeActionOriginalType = readRuntimeActionOriginalType(ambiguousHire);
|
|
7277
|
+
error.runtimeActionReason = error.message;
|
|
7278
|
+
throw error;
|
|
7279
|
+
}
|
|
6955
7280
|
const ambiguousChild = actions.find(runtimeActionIsAmbiguousApprovalGatedChild);
|
|
6956
7281
|
if (!ambiguousChild) return;
|
|
6957
7282
|
const error = new Error(
|
|
@@ -7006,9 +7331,44 @@ function assertRuntimeActionsAllowedByContinuationScope(actions, scope) {
|
|
|
7006
7331
|
}
|
|
7007
7332
|
}
|
|
7008
7333
|
|
|
7009
|
-
function preflightRuntimeAction(action, preflightContext) {
|
|
7334
|
+
function preflightRuntimeAction(action, preflightContext, actionIndex) {
|
|
7010
7335
|
const type = readRuntimeActionType(action);
|
|
7011
7336
|
const fields = runtimeActionBusinessFields(action);
|
|
7337
|
+
assertNoUnsupportedRuntimeActionFields(action, actionIndex);
|
|
7338
|
+
assertRuntimeActionFieldTypes(action, actionIndex);
|
|
7339
|
+
if (type === "create_child_task") {
|
|
7340
|
+
assertNoPlaceholderRuntimeActionText(fields, ["title", "description"]);
|
|
7341
|
+
assertRuntimeActionValid(readBoundedActionString(fields, "title", 240), "create_child_task action requires title");
|
|
7342
|
+
if (fields.status !== undefined) {
|
|
7343
|
+
assertRuntimeActionValid(readActionStatus(fields.status), `create_child_task status is unsupported: ${String(fields.status)}`);
|
|
7344
|
+
}
|
|
7345
|
+
if (fields.priority !== undefined) {
|
|
7346
|
+
assertRuntimeActionValid(readActionPriority(fields.priority), `create_child_task priority is unsupported: ${String(fields.priority)}`);
|
|
7347
|
+
}
|
|
7348
|
+
assertRuntimeActionValid(
|
|
7349
|
+
!readString(fields.assigneeAgentId) || (!readString(fields.assigneeAgentClientKey) && !readString(fields.agentClientKey)),
|
|
7350
|
+
"create_child_task action cannot combine assigneeAgentId with assigneeAgentClientKey/agentClientKey",
|
|
7351
|
+
);
|
|
7352
|
+
if (fields.blockedByIssueIds !== undefined) {
|
|
7353
|
+
for (const [index, rawIssueId] of fields.blockedByIssueIds.entries()) {
|
|
7354
|
+
const issueId = readString(rawIssueId);
|
|
7355
|
+
assertRuntimeActionValid(
|
|
7356
|
+
issueId && /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(issueId),
|
|
7357
|
+
`create_child_task blockedByIssueIds[${index}] must be a UUID`,
|
|
7358
|
+
);
|
|
7359
|
+
}
|
|
7360
|
+
}
|
|
7361
|
+
return;
|
|
7362
|
+
}
|
|
7363
|
+
if (["agent_hire", "create_agent"].includes(type ?? "")) {
|
|
7364
|
+
assertNoPlaceholderRuntimeActionText(fields, ["name", "role", "title", "instructionsBundle"]);
|
|
7365
|
+
assertRuntimeActionValid(readBoundedActionString(fields, "name", 240), `${type} action requires name`);
|
|
7366
|
+
assertRuntimeActionValid(
|
|
7367
|
+
!readString(fields.reportsTo) || !readString(fields.reportsToAgentClientKey),
|
|
7368
|
+
`${type} action cannot combine reportsTo with reportsToAgentClientKey`,
|
|
7369
|
+
);
|
|
7370
|
+
return;
|
|
7371
|
+
}
|
|
7012
7372
|
if (type === "add_comment") {
|
|
7013
7373
|
assertNoPlaceholderRuntimeActionText(fields, ["body", "content", "text", "message"]);
|
|
7014
7374
|
const body = readBoundedActionString(fields, "body", 20_000)
|
|
@@ -7019,7 +7379,38 @@ function preflightRuntimeAction(action, preflightContext) {
|
|
|
7019
7379
|
return;
|
|
7020
7380
|
}
|
|
7021
7381
|
if (type === "upload_artifact") {
|
|
7382
|
+
assertNoPlaceholderRuntimeActionText(fields, ["path", "filePath", "artifactPath", "artifact_path", "title", "summary"]);
|
|
7383
|
+
assertRuntimeActionValid(readRuntimeArtifactPath(fields), "upload_artifact action requires path");
|
|
7384
|
+
const pathFields = ["path", "filePath", "artifactPath", "artifact_path"].filter((field) => readString(fields[field]));
|
|
7385
|
+
assertRuntimeActionValid(pathFields.length === 1, "upload_artifact action requires exactly one path field");
|
|
7386
|
+
const sourceWorkProductId = readString(fields.sourceWorkProductId);
|
|
7387
|
+
const sourceWorkProductIds = Array.isArray(fields.sourceWorkProductIds)
|
|
7388
|
+
? fields.sourceWorkProductIds.map(readString).filter(Boolean)
|
|
7389
|
+
: [];
|
|
7390
|
+
const supersedesWorkProductId = readString(fields.supersedesWorkProductId);
|
|
7391
|
+
assertRuntimeActionValid(
|
|
7392
|
+
Boolean(sourceWorkProductId) === Boolean(supersedesWorkProductId),
|
|
7393
|
+
"upload_artifact parent replacement requires both sourceWorkProductId and supersedesWorkProductId",
|
|
7394
|
+
);
|
|
7395
|
+
const evidenceRole = readString(fields.evidenceRole);
|
|
7396
|
+
assertRuntimeActionValid(
|
|
7397
|
+
!evidenceRole || ["source", "derived", "review"].includes(evidenceRole),
|
|
7398
|
+
`upload_artifact evidenceRole is unsupported: ${String(fields.evidenceRole)}`,
|
|
7399
|
+
);
|
|
7400
|
+
assertRuntimeActionValid(
|
|
7401
|
+
!["derived", "review"].includes(evidenceRole ?? "") || sourceWorkProductIds.length > 0 || Boolean(sourceWorkProductId),
|
|
7402
|
+
`upload_artifact evidenceRole=${evidenceRole} requires sourceWorkProductIds`,
|
|
7403
|
+
);
|
|
7404
|
+
if (fields.sourceWorkProductIds !== undefined) {
|
|
7405
|
+
assertRuntimeActionValid(Array.isArray(fields.sourceWorkProductIds), "upload_artifact sourceWorkProductIds must be an array");
|
|
7406
|
+
assertRuntimeActionValid(sourceWorkProductIds.length === fields.sourceWorkProductIds.length, "upload_artifact sourceWorkProductIds must contain non-empty strings");
|
|
7407
|
+
}
|
|
7408
|
+
if (fields.scenarioKeys !== undefined) {
|
|
7409
|
+
assertRuntimeActionValid(Array.isArray(fields.scenarioKeys), "upload_artifact scenarioKeys must be an array");
|
|
7410
|
+
assertRuntimeActionValid(fields.scenarioKeys.map(readString).filter(Boolean).length === fields.scenarioKeys.length, "upload_artifact scenarioKeys must contain non-empty strings");
|
|
7411
|
+
}
|
|
7022
7412
|
assertRuntimeArtifactContentType(fields, preflightContext.allowedContentTypes);
|
|
7413
|
+
resolveWorkspaceArtifactPath(fields, { cwd: preflightContext.cwd });
|
|
7023
7414
|
return;
|
|
7024
7415
|
}
|
|
7025
7416
|
if (type === "create_interaction") {
|
|
@@ -7030,6 +7421,10 @@ function preflightRuntimeAction(action, preflightContext) {
|
|
|
7030
7421
|
["request_confirmation", "request_checkbox_confirmation", "ask_user_questions", "suggest_tasks"].includes(kind),
|
|
7031
7422
|
`create_interaction action does not support kind: ${kind}`,
|
|
7032
7423
|
);
|
|
7424
|
+
assertRuntimeActionValid(
|
|
7425
|
+
kind !== "ask_user_questions" || readString(fields.continuationPolicy) !== "wake_assignee_on_accept",
|
|
7426
|
+
"ask_user_questions continuationPolicy cannot be wake_assignee_on_accept; use wake_assignee so answering the questions resumes the assignee",
|
|
7427
|
+
);
|
|
7033
7428
|
const payload = normalizeCreateInteractionPayload(fields, kind);
|
|
7034
7429
|
assertRuntimeActionValid(
|
|
7035
7430
|
Object.keys(payload).length > 0,
|
|
@@ -7044,6 +7439,20 @@ function preflightRuntimeAction(action, preflightContext) {
|
|
|
7044
7439
|
const status = readActionStatus(fields.status);
|
|
7045
7440
|
const comment = readBoundedActionString(fields, "comment", 20_000);
|
|
7046
7441
|
assertRuntimeActionValid(status || comment, "update_parent action requires status or comment");
|
|
7442
|
+
if (fields.status !== undefined) {
|
|
7443
|
+
assertRuntimeActionValid(status, `update_parent status is unsupported: ${String(fields.status)}`);
|
|
7444
|
+
}
|
|
7445
|
+
if (
|
|
7446
|
+
status === "done" &&
|
|
7447
|
+
runtimeActionDoneNeedsExternalHuman(fields, preflightContext) &&
|
|
7448
|
+
!preflightContext.createdHumanOwnedInputPath &&
|
|
7449
|
+
fields.force !== true
|
|
7450
|
+
) {
|
|
7451
|
+
assertRuntimeActionValid(
|
|
7452
|
+
false,
|
|
7453
|
+
"external_input_disposition_required: external input cannot be represented only by a free-text owner; create a human-owned blocking child task or another first-class external dependency path",
|
|
7454
|
+
);
|
|
7455
|
+
}
|
|
7047
7456
|
if (
|
|
7048
7457
|
status === "in_review" &&
|
|
7049
7458
|
!preflightContext.createdReviewPathForInReview &&
|
|
@@ -7056,22 +7465,43 @@ function preflightRuntimeAction(action, preflightContext) {
|
|
|
7056
7465
|
) {
|
|
7057
7466
|
preflightContext.createdReviewPathForInReview = true;
|
|
7058
7467
|
}
|
|
7468
|
+
return;
|
|
7469
|
+
}
|
|
7470
|
+
if (type === "update_company_profile") {
|
|
7471
|
+
const profileFields = ["tagline", "stage", "targetCustomers", "products", "currentPriority", "businessModel", "regions", "brandTone"];
|
|
7472
|
+
assertRuntimeActionValid(
|
|
7473
|
+
profileFields.some((field) => fields[field] === null || readBoundedActionString(fields, field, 2000)),
|
|
7474
|
+
"update_company_profile action requires at least one profile field",
|
|
7475
|
+
);
|
|
7476
|
+
return;
|
|
7477
|
+
}
|
|
7478
|
+
if (type === "create_routine") {
|
|
7479
|
+
assertRuntimeActionValid(readBoundedActionString(fields, "title", 240), "create_routine action requires title");
|
|
7480
|
+
assertRuntimeActionValid(readBoundedActionString(fields, "cronExpression", 120), "create_routine action requires cronExpression");
|
|
7481
|
+
if (fields.priority !== undefined) {
|
|
7482
|
+
assertRuntimeActionValid(readActionPriority(fields.priority), `create_routine priority is unsupported: ${String(fields.priority)}`);
|
|
7483
|
+
}
|
|
7059
7484
|
}
|
|
7060
7485
|
}
|
|
7061
7486
|
|
|
7062
7487
|
function preflightRuntimeActionBatch(actions, options = {}) {
|
|
7063
|
-
|
|
7064
|
-
|
|
7488
|
+
const actionIndexByObject = new Map(actions.map((action, index) => [action, index]));
|
|
7489
|
+
const orderedActions = orderRuntimeActionsByClientKeyDependencies(actions);
|
|
7490
|
+
assertRuntimeActionsAllowedByContinuationScope(orderedActions, options.continuationScope);
|
|
7491
|
+
assertNoAmbiguousApprovalGatedSideEffects(orderedActions);
|
|
7065
7492
|
const preflightContext = {
|
|
7066
7493
|
createdReviewPathForInReview: false,
|
|
7067
7494
|
createdRootBlockingChildForInReview: false,
|
|
7495
|
+
createdHumanOwnedInputPath: false,
|
|
7068
7496
|
answeredQuestionContinuation: options.answeredQuestionContinuation === true,
|
|
7069
7497
|
allowedContentTypes: Array.isArray(options.allowedContentTypes) ? options.allowedContentTypes : [],
|
|
7498
|
+
cwd: readString(options.cwd),
|
|
7070
7499
|
finalCommentBodies: [],
|
|
7071
7500
|
};
|
|
7072
|
-
for (const action of
|
|
7501
|
+
for (const action of orderedActions) {
|
|
7502
|
+
const actionIndex = actionIndexByObject.get(action);
|
|
7073
7503
|
try {
|
|
7074
|
-
preflightRuntimeAction(action, preflightContext);
|
|
7504
|
+
preflightRuntimeAction(action, preflightContext, actionIndex);
|
|
7075
7505
|
const fields = runtimeActionBusinessFields(action);
|
|
7076
7506
|
if (readRuntimeActionType(fields) === "add_comment") {
|
|
7077
7507
|
const body = readBoundedActionString(fields, "body", 20_000)
|
|
@@ -7083,6 +7513,9 @@ function preflightRuntimeActionBatch(actions, options = {}) {
|
|
|
7083
7513
|
if (willRuntimeActionCreateRootBlockingChild(action)) {
|
|
7084
7514
|
preflightContext.createdRootBlockingChildForInReview = true;
|
|
7085
7515
|
}
|
|
7516
|
+
if (willRuntimeActionCreateHumanOwnedInputPath(action)) {
|
|
7517
|
+
preflightContext.createdHumanOwnedInputPath = true;
|
|
7518
|
+
}
|
|
7086
7519
|
if (willRuntimeActionCreateReviewPath(action)) {
|
|
7087
7520
|
preflightContext.createdReviewPathForInReview = true;
|
|
7088
7521
|
}
|
|
@@ -7099,6 +7532,40 @@ function preflightRuntimeActionBatch(actions, options = {}) {
|
|
|
7099
7532
|
throw err;
|
|
7100
7533
|
}
|
|
7101
7534
|
}
|
|
7535
|
+
return orderedActions;
|
|
7536
|
+
}
|
|
7537
|
+
|
|
7538
|
+
function runtimeActionAuthorizationClass(action) {
|
|
7539
|
+
// Keep these high-risk route mappings aligned with server-side checks in
|
|
7540
|
+
// routes/agents.ts (organization_mutation) and routes/issues.ts
|
|
7541
|
+
// (executable_child). Unknown action types intentionally remain governance.
|
|
7542
|
+
const fields = runtimeActionBusinessFields(action);
|
|
7543
|
+
const type = readRuntimeActionType(fields);
|
|
7544
|
+
if (["agent_hire", "create_agent"].includes(type ?? "")) return "organization_mutation";
|
|
7545
|
+
if (
|
|
7546
|
+
type === "create_child_task" &&
|
|
7547
|
+
["todo", "in_progress"].includes(readActionStatus(fields.status) ?? "todo")
|
|
7548
|
+
) return "executable_child";
|
|
7549
|
+
return "task_governance";
|
|
7550
|
+
}
|
|
7551
|
+
|
|
7552
|
+
function assertRuntimeActionsAllowedByAuthorizationEnvelope(actions, envelope) {
|
|
7553
|
+
for (const [actionIndex, action] of actions.entries()) {
|
|
7554
|
+
const requiredAuthorization = runtimeActionAuthorizationClass(action);
|
|
7555
|
+
if (envelope.allowedActionClasses.has(requiredAuthorization)) continue;
|
|
7556
|
+
const type = readRuntimeActionType(runtimeActionBusinessFields(action)) ?? "unknown";
|
|
7557
|
+
const err = runtimeActionValidationError(
|
|
7558
|
+
`${type} action at index ${actionIndex} requires authorization class ${requiredAuthorization}; ` +
|
|
7559
|
+
`source ${envelope.sourceKind ?? "unknown"}:${envelope.sourceId ?? "unknown"} revision ${envelope.sourceRevision ?? "unknown"}`,
|
|
7560
|
+
);
|
|
7561
|
+
err.runtimeActionType = type;
|
|
7562
|
+
err.runtimeActionFieldPath = `actions[${actionIndex}]`;
|
|
7563
|
+
err.runtimeActionRequiredAuthorization = requiredAuthorization;
|
|
7564
|
+
err.runtimeActionAuthorizationSourceKind = envelope.sourceKind;
|
|
7565
|
+
err.runtimeActionAuthorizationSourceId = envelope.sourceId;
|
|
7566
|
+
err.runtimeActionAuthorizationSourceRevision = envelope.sourceRevision;
|
|
7567
|
+
throw err;
|
|
7568
|
+
}
|
|
7102
7569
|
}
|
|
7103
7570
|
|
|
7104
7571
|
function normalizeRuntimeActionPlaceholderText(value) {
|
|
@@ -7201,6 +7668,7 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
7201
7668
|
priority: readActionPriority(fields.priority) ?? "medium",
|
|
7202
7669
|
assigneeAgentId: runtimeChildAssigneeAgentId(fields, runtimeAuth, command, actionContext),
|
|
7203
7670
|
assigneeUserId: readString(fields.assigneeUserId),
|
|
7671
|
+
blockedByIssueIds: runtimeActionBlockedByIssueIds(fields, actionContext),
|
|
7204
7672
|
blockParentUntilDone,
|
|
7205
7673
|
originKind: "amaster_runtime_child_task",
|
|
7206
7674
|
originId: issueId,
|
|
@@ -7211,6 +7679,23 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
7211
7679
|
idempotencyKey: originFingerprint,
|
|
7212
7680
|
});
|
|
7213
7681
|
const createdIssueId = readString(asRecord(created).id);
|
|
7682
|
+
const persistedAssigneeAgentId = readString(asRecord(created).assigneeAgentId);
|
|
7683
|
+
if (body.assigneeAgentId && persistedAssigneeAgentId !== body.assigneeAgentId) {
|
|
7684
|
+
throw new Error(
|
|
7685
|
+
`runtime_action_postcondition_failed: child assignee mismatch; expected ${body.assigneeAgentId}, received ${persistedAssigneeAgentId ?? "missing"}`,
|
|
7686
|
+
);
|
|
7687
|
+
}
|
|
7688
|
+
if (body.blockedByIssueIds.length > 0) {
|
|
7689
|
+
const persistedBlockerIds = Array.isArray(asRecord(created).blockedBy)
|
|
7690
|
+
? asRecord(created).blockedBy.map((entry) => readString(asRecord(entry).id)).filter(Boolean)
|
|
7691
|
+
: [];
|
|
7692
|
+
const missingBlockerIds = body.blockedByIssueIds.filter((blockerId) => !persistedBlockerIds.includes(blockerId));
|
|
7693
|
+
if (missingBlockerIds.length > 0) {
|
|
7694
|
+
throw new Error(
|
|
7695
|
+
`runtime_action_postcondition_failed: child blockers were not persisted: ${missingBlockerIds.join(", ")}`,
|
|
7696
|
+
);
|
|
7697
|
+
}
|
|
7698
|
+
}
|
|
7214
7699
|
const clientKey = runtimeActionResultKey(fields);
|
|
7215
7700
|
if (createdIssueId) actionContext.createdChildIssueIdByFingerprint.set(duplicateKey, createdIssueId);
|
|
7216
7701
|
if (createdIssueId && clientKey) actionContext.issueIdByClientKey.set(clientKey, createdIssueId);
|
|
@@ -7250,6 +7735,8 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
7250
7735
|
adapterType: runtimeActionCurrentAgentAdapterType(command),
|
|
7251
7736
|
runtimeConfig: runtimeActionCurrentAgentRuntimeConnector(command),
|
|
7252
7737
|
});
|
|
7738
|
+
const reportsTo = runtimeActionReportsToAgentId(fields, actionContext);
|
|
7739
|
+
if (reportsTo) body.reportsTo = reportsTo;
|
|
7253
7740
|
const idempotencyKey = runtimeAgentHireActionFingerprint(fields, companyId, issueId, body);
|
|
7254
7741
|
body.idempotencyKey = idempotencyKey;
|
|
7255
7742
|
const created = asRecord(await runtimeApiJson(
|
|
@@ -7263,6 +7750,11 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
7263
7750
|
const approval = asRecord(created.approval);
|
|
7264
7751
|
const clientKey = runtimeActionResultKey(fields);
|
|
7265
7752
|
const agentId = readString(agent.id);
|
|
7753
|
+
if (reportsTo && readString(agent.reportsTo) !== reportsTo) {
|
|
7754
|
+
throw new Error(
|
|
7755
|
+
`runtime_action_postcondition_failed: agent reportsTo mismatch; expected ${reportsTo}, received ${readString(agent.reportsTo) ?? "missing"}`,
|
|
7756
|
+
);
|
|
7757
|
+
}
|
|
7266
7758
|
if (agentId && clientKey) actionContext.agentIdByClientKey.set(clientKey, agentId);
|
|
7267
7759
|
return {
|
|
7268
7760
|
type,
|
|
@@ -7308,25 +7800,6 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
7308
7800
|
{ key: "idempotencyKey", placeholders: ["artifact-key", "artifact key", "upload-artifact-key", "upload artifact key"] },
|
|
7309
7801
|
]);
|
|
7310
7802
|
const rawPath = readRuntimeArtifactPath(fields);
|
|
7311
|
-
if (!rawPath) {
|
|
7312
|
-
const title = readBoundedActionString(fields, "title", 240) ?? "运行产物";
|
|
7313
|
-
const summary = readBoundedActionString(fields, "summary", 20_000);
|
|
7314
|
-
const body = [
|
|
7315
|
-
`AMaster 未上传产物 \`${title}\`,因为 runtime action 没有提供工作区内文件路径。`,
|
|
7316
|
-
summary ? `\n${summary}` : "",
|
|
7317
|
-
"\n请在后续运行中先把文件写入执行工作区,再用 upload_artifact.path 引用该相对路径。",
|
|
7318
|
-
].join("");
|
|
7319
|
-
const created = await runtimeApiJson(runtimeAuth, "POST", `/api/issues/${encodeURIComponent(issueId)}/comments`, { body });
|
|
7320
|
-
actionContext.finalCommentBodies.push(body);
|
|
7321
|
-
return {
|
|
7322
|
-
type,
|
|
7323
|
-
...(originalType && originalType !== type ? { originalType } : {}),
|
|
7324
|
-
status: "skipped",
|
|
7325
|
-
reason: "missing_path",
|
|
7326
|
-
title,
|
|
7327
|
-
commentId: readString(asRecord(created).id),
|
|
7328
|
-
};
|
|
7329
|
-
}
|
|
7330
7803
|
const payload = asRecord(command.payload);
|
|
7331
7804
|
const companyId = readString(payload.companyId) ?? readString(runtimeAuth.companyId);
|
|
7332
7805
|
if (!companyId) throw new Error("upload_artifact action requires companyId");
|
|
@@ -7348,7 +7821,7 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
7348
7821
|
const title = readBoundedActionString(fields, "title", 240) ?? filename;
|
|
7349
7822
|
if (fields.createWorkProduct !== false) {
|
|
7350
7823
|
const existingWorkProducts = await listExistingIssueWorkProducts(runtimeAuth, issueId);
|
|
7351
|
-
const existingWorkProduct = existingExplicitArtifactUploadMatch(filePath, title, existingWorkProducts);
|
|
7824
|
+
const existingWorkProduct = existingExplicitArtifactUploadMatch(filePath, title, existingWorkProducts, fields);
|
|
7352
7825
|
if (existingWorkProduct) {
|
|
7353
7826
|
const metadata = asRecord(existingWorkProduct.metadata);
|
|
7354
7827
|
return {
|
|
@@ -7401,8 +7874,16 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
7401
7874
|
openPath,
|
|
7402
7875
|
downloadPath,
|
|
7403
7876
|
originalFilename: readString(attachment.originalFilename) ?? filename,
|
|
7877
|
+
...(readString(fields.sourceWorkProductId) ? { sourceWorkProductId: readString(fields.sourceWorkProductId) } : {}),
|
|
7878
|
+
...(Array.isArray(fields.sourceWorkProductIds) ? { sourceWorkProductIds: fields.sourceWorkProductIds.map(readString).filter(Boolean) } : {}),
|
|
7879
|
+
...(readString(fields.supersedesWorkProductId) ? { supersedesWorkProductId: readString(fields.supersedesWorkProductId) } : {}),
|
|
7880
|
+
...(readString(fields.evidenceRole) ? { evidenceRole: readString(fields.evidenceRole) } : {}),
|
|
7881
|
+
...(Array.isArray(fields.scenarioKeys) ? { scenarioKeys: fields.scenarioKeys.map(readString).filter(Boolean) } : {}),
|
|
7404
7882
|
},
|
|
7405
7883
|
}));
|
|
7884
|
+
if (!readString(workProduct.id)) {
|
|
7885
|
+
throw new Error("runtime_action_postcondition_failed: artifact work product response did not include id");
|
|
7886
|
+
}
|
|
7406
7887
|
}
|
|
7407
7888
|
|
|
7408
7889
|
return {
|
|
@@ -7475,18 +7956,6 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
7475
7956
|
if (Object.keys(patch).length === 0) {
|
|
7476
7957
|
throw new Error("update_parent action requires status or comment");
|
|
7477
7958
|
}
|
|
7478
|
-
if (status === "done" && runtimeActionDoneNeedsExternalHuman(fields, actionContext) && fields.force !== true) {
|
|
7479
|
-
Object.assign(patch, runtimeActionExternalHumanPatch(comment));
|
|
7480
|
-
const updated = await runtimeApiJson(runtimeAuth, "PATCH", `/api/issues/${encodeURIComponent(issueId)}`, patch);
|
|
7481
|
-
return {
|
|
7482
|
-
type,
|
|
7483
|
-
...(originalType && originalType !== type ? { originalType } : {}),
|
|
7484
|
-
issueId: readString(asRecord(updated).id),
|
|
7485
|
-
status: readString(asRecord(updated).status) ?? "blocked",
|
|
7486
|
-
coercedFromStatus: "done",
|
|
7487
|
-
externalOwnerAction: true,
|
|
7488
|
-
};
|
|
7489
|
-
}
|
|
7490
7959
|
if (status === "done" && actionContext.createdReviewPathForInReview && fields.force !== true) {
|
|
7491
7960
|
patch.status = "in_review";
|
|
7492
7961
|
patch.comment = comment
|
|
@@ -7742,6 +8211,48 @@ function runtimeActionFailureSummary(action, err) {
|
|
|
7742
8211
|
};
|
|
7743
8212
|
}
|
|
7744
8213
|
|
|
8214
|
+
function runtimeActionLedgerResourceIds(result) {
|
|
8215
|
+
const record = asRecord(result);
|
|
8216
|
+
const resourceIds = {};
|
|
8217
|
+
for (const key of ["issueId", "parentIssueId", "agentId", "approvalId", "commentId", "interactionId", "attachmentId", "workProductId", "routineId", "profileId"]) {
|
|
8218
|
+
const value = readString(record[key]);
|
|
8219
|
+
if (value) resourceIds[key] = value;
|
|
8220
|
+
}
|
|
8221
|
+
return resourceIds;
|
|
8222
|
+
}
|
|
8223
|
+
|
|
8224
|
+
function runtimeActionLedgerPostconditionStatus(action, status, error) {
|
|
8225
|
+
if (status === "failed") {
|
|
8226
|
+
return /runtime_action_postcondition_failed/i.test(String(error instanceof Error ? error.message : error ?? ""))
|
|
8227
|
+
? "failed"
|
|
8228
|
+
: "not_run";
|
|
8229
|
+
}
|
|
8230
|
+
if (status !== "applied" && status !== "reused") return "not_run";
|
|
8231
|
+
return ["create_child_task", "agent_hire", "create_agent", "upload_artifact"].includes(
|
|
8232
|
+
readRuntimeActionType(action) ?? "",
|
|
8233
|
+
)
|
|
8234
|
+
? "passed"
|
|
8235
|
+
: "not_applicable";
|
|
8236
|
+
}
|
|
8237
|
+
|
|
8238
|
+
function runtimeActionLedgerEntry(action, actionIndex, status, result = null, error = null) {
|
|
8239
|
+
const fields = runtimeActionBusinessFields(action);
|
|
8240
|
+
const resultRecord = asRecord(result);
|
|
8241
|
+
const clientKey = readString(fields.clientKey);
|
|
8242
|
+
const resourceIds = runtimeActionLedgerResourceIds(resultRecord);
|
|
8243
|
+
return {
|
|
8244
|
+
actionIndex,
|
|
8245
|
+
actionType: readRuntimeActionType(fields) ?? "unknown",
|
|
8246
|
+
actionKey: clientKey ?? `index:${actionIndex}`,
|
|
8247
|
+
...(clientKey ? { clientKey } : {}),
|
|
8248
|
+
status,
|
|
8249
|
+
reusedExisting: resultRecord.reusedExisting === true,
|
|
8250
|
+
postconditionStatus: runtimeActionLedgerPostconditionStatus(fields, status, error),
|
|
8251
|
+
...(Object.keys(resourceIds).length > 0 ? { resourceIds } : {}),
|
|
8252
|
+
...(error ? { error: truncateText(error instanceof Error ? error.message : String(error), 1000) } : {}),
|
|
8253
|
+
};
|
|
8254
|
+
}
|
|
8255
|
+
|
|
7745
8256
|
function isSkippedRuntimeActionResult(result) {
|
|
7746
8257
|
return readString(asRecord(result).status) === "skipped";
|
|
7747
8258
|
}
|
|
@@ -7922,6 +8433,12 @@ function runtimeActionDiagnostics(err, options = {}) {
|
|
|
7922
8433
|
? { decompositionMatch: err.runtimeActionDecompositionMatch }
|
|
7923
8434
|
: {}),
|
|
7924
8435
|
...(err?.runtimeActionInferenceTrace ? { inferenceTrace: err.runtimeActionInferenceTrace } : {}),
|
|
8436
|
+
...(readString(err?.runtimeActionFieldPath) ? { fieldPath: readString(err.runtimeActionFieldPath) } : {}),
|
|
8437
|
+
...(readString(err?.runtimeActionReceivedType) ? { receivedType: readString(err.runtimeActionReceivedType) } : {}),
|
|
8438
|
+
...(readString(err?.runtimeActionRequiredAuthorization) ? { requiredAuthorization: readString(err.runtimeActionRequiredAuthorization) } : {}),
|
|
8439
|
+
...(readString(err?.runtimeActionAuthorizationSourceKind) ? { authorizationSourceKind: readString(err.runtimeActionAuthorizationSourceKind) } : {}),
|
|
8440
|
+
...(readString(err?.runtimeActionAuthorizationSourceId) ? { authorizationSourceId: readString(err.runtimeActionAuthorizationSourceId) } : {}),
|
|
8441
|
+
...(readString(err?.runtimeActionAuthorizationSourceRevision) ? { authorizationSourceRevision: readString(err.runtimeActionAuthorizationSourceRevision) } : {}),
|
|
7925
8442
|
...(Array.isArray(err?.runtimeActionAllowedContentTypes)
|
|
7926
8443
|
? { allowedContentTypes: err.runtimeActionAllowedContentTypes }
|
|
7927
8444
|
: {}),
|
|
@@ -7931,6 +8448,7 @@ function runtimeActionDiagnostics(err, options = {}) {
|
|
|
7931
8448
|
...(err?.runtimeActionVerificationResult
|
|
7932
8449
|
? { verificationResult: err.runtimeActionVerificationResult }
|
|
7933
8450
|
: {}),
|
|
8451
|
+
...(err?.runtimeActionLedger ? { actionLedger: err.runtimeActionLedger } : {}),
|
|
7934
8452
|
...(runtimeActionExpectedShape(actionType, actionKind) ? { expectedShape: runtimeActionExpectedShape(actionType, actionKind) } : {}),
|
|
7935
8453
|
...(typeof err?.httpStatus === "number" ? { backendStatus: err.httpStatus } : {}),
|
|
7936
8454
|
...(backendErrors.length > 0 ? { backendErrors } : {}),
|
|
@@ -7967,7 +8485,7 @@ function classifyRuntimeActionFailure(err) {
|
|
|
7967
8485
|
}
|
|
7968
8486
|
if (actionType === "output_channel") {
|
|
7969
8487
|
return {
|
|
7970
|
-
errorCode: "runtime_action_validation_failed",
|
|
8488
|
+
errorCode: readString(err?.runtimeActionErrorCode) ?? "runtime_action_validation_failed",
|
|
7971
8489
|
errorFamily: "output_contract",
|
|
7972
8490
|
...details,
|
|
7973
8491
|
...(diagnostics ? { runtimeActionDiagnostics: diagnostics } : {}),
|
|
@@ -7984,7 +8502,10 @@ function classifyRuntimeActionFailure(err) {
|
|
|
7984
8502
|
...(diagnostics ? { runtimeActionDiagnostics: diagnostics } : {}),
|
|
7985
8503
|
};
|
|
7986
8504
|
}
|
|
7987
|
-
if (
|
|
8505
|
+
if (
|
|
8506
|
+
actionType === "upload_artifact" &&
|
|
8507
|
+
/unsupported artifact content type|requires path|file does not exist|path must be inside/i.test(haystack)
|
|
8508
|
+
) {
|
|
7988
8509
|
return {
|
|
7989
8510
|
errorCode: "runtime_action_validation_failed",
|
|
7990
8511
|
errorFamily: "output_contract",
|
|
@@ -8008,6 +8529,31 @@ function classifyRuntimeActionFailure(err) {
|
|
|
8008
8529
|
...(diagnostics ? { runtimeActionDiagnostics: diagnostics } : {}),
|
|
8009
8530
|
};
|
|
8010
8531
|
}
|
|
8532
|
+
if (/runtime_action_postcondition_failed/i.test(haystack)) {
|
|
8533
|
+
return {
|
|
8534
|
+
errorCode: "runtime_action_postcondition_failed",
|
|
8535
|
+
errorFamily: "output_contract",
|
|
8536
|
+
...details,
|
|
8537
|
+
...(diagnostics ? { runtimeActionDiagnostics: diagnostics } : {}),
|
|
8538
|
+
};
|
|
8539
|
+
}
|
|
8540
|
+
if (err?.httpStatus === 401) {
|
|
8541
|
+
const authenticationDiagnostics = runtimeActionDiagnostics(err, { retryable: true });
|
|
8542
|
+
return {
|
|
8543
|
+
errorCode: "runtime_action_authentication_failed",
|
|
8544
|
+
errorFamily: "authentication",
|
|
8545
|
+
...details,
|
|
8546
|
+
...(authenticationDiagnostics ? { runtimeActionDiagnostics: authenticationDiagnostics } : {}),
|
|
8547
|
+
};
|
|
8548
|
+
}
|
|
8549
|
+
if (err?.httpStatus === 403) {
|
|
8550
|
+
return {
|
|
8551
|
+
errorCode: "runtime_action_permission_denied",
|
|
8552
|
+
errorFamily: "permission",
|
|
8553
|
+
...details,
|
|
8554
|
+
...(diagnostics ? { runtimeActionDiagnostics: diagnostics } : {}),
|
|
8555
|
+
};
|
|
8556
|
+
}
|
|
8011
8557
|
if (/approval-gated executable child tasks/i.test(haystack)) {
|
|
8012
8558
|
return {
|
|
8013
8559
|
errorCode: "runtime_action_validation_failed",
|
|
@@ -8312,9 +8858,19 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
|
|
|
8312
8858
|
err.runtimeActionReasonHint = "assistant_message_required";
|
|
8313
8859
|
throw err;
|
|
8314
8860
|
}
|
|
8861
|
+
if (!envelope && hasMalformedRuntimeActionMarkerIntent(actionText)) {
|
|
8862
|
+
const err = runtimeActionValidationError(
|
|
8863
|
+
"AMASTER_RUNTIME_ACTIONS action intent used a non-canonical marker; the marker must be an unformatted bare line",
|
|
8864
|
+
);
|
|
8865
|
+
err.runtimeActionType = "output_channel";
|
|
8866
|
+
err.runtimeActionReasonHint = "bare_marker_line_required";
|
|
8867
|
+
err.runtimeActionErrorCode = "runtime_action_output_marker_invalid";
|
|
8868
|
+
throw err;
|
|
8869
|
+
}
|
|
8315
8870
|
if (!envelope) return null;
|
|
8316
8871
|
const rawActions = Array.isArray(envelope.actions) ? envelope.actions.map(asRecord) : [];
|
|
8317
8872
|
const continuationScope = runtimeActionContinuationScope(command);
|
|
8873
|
+
const authorizationEnvelope = runtimeActionAuthorizationEnvelope(command);
|
|
8318
8874
|
assertRuntimeActionsAllowedByContinuationScope(rawActions, continuationScope);
|
|
8319
8875
|
assertExplicitTaskDecomposition(command, rawActions);
|
|
8320
8876
|
if (
|
|
@@ -8327,6 +8883,10 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
|
|
|
8327
8883
|
const runtimeAuth = runtimeActionAuthForCommand(command);
|
|
8328
8884
|
const issueId = readString(runtimeAuth.issueId) ?? commandIssueId(command);
|
|
8329
8885
|
if (!issueId) throw new Error("AMaster runtime action bridge requires a current issue id");
|
|
8886
|
+
const batchFingerprint = `runtime-action-batch:${stableRuntimeActionHash({
|
|
8887
|
+
runId: runtimeActionRunId(runtimeAuth) ?? commandRunId(command),
|
|
8888
|
+
envelope,
|
|
8889
|
+
})}`;
|
|
8330
8890
|
const allowedContentTypes = runtimeAttachmentContentTypes(command);
|
|
8331
8891
|
let deliverableManifest;
|
|
8332
8892
|
try {
|
|
@@ -8358,20 +8918,39 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
|
|
|
8358
8918
|
attachRuntimeCheckpointCandidates(err, envelope, cwd);
|
|
8359
8919
|
throw err;
|
|
8360
8920
|
}
|
|
8361
|
-
|
|
8921
|
+
let actions = orderRuntimeActionsForGovernance([...generatedUploadActions, ...rawActions]);
|
|
8362
8922
|
const answeredQuestionContinuation = runtimeActionAnsweredQuestionContinuation(command);
|
|
8363
8923
|
try {
|
|
8364
|
-
preflightRuntimeActionBatch(actions, {
|
|
8924
|
+
actions = preflightRuntimeActionBatch(actions, {
|
|
8365
8925
|
answeredQuestionContinuation,
|
|
8366
8926
|
allowedContentTypes,
|
|
8367
8927
|
continuationScope,
|
|
8928
|
+
cwd,
|
|
8368
8929
|
});
|
|
8930
|
+
assertRuntimeActionsAllowedByAuthorizationEnvelope(actions, authorizationEnvelope);
|
|
8369
8931
|
} catch (err) {
|
|
8932
|
+
if (err && typeof err === "object" && !err.runtimeActionLedger) {
|
|
8933
|
+
const fieldPath = readString(err.runtimeActionFieldPath);
|
|
8934
|
+
const failedIndex = Number.parseInt(fieldPath?.match(/^actions\[(\d+)]/)?.[1] ?? "0", 10);
|
|
8935
|
+
err.runtimeActionLedger = {
|
|
8936
|
+
batchFingerprint,
|
|
8937
|
+
traceId: batchFingerprint,
|
|
8938
|
+
status: "validation_failed",
|
|
8939
|
+
actions: actions.map((action, index) => runtimeActionLedgerEntry(
|
|
8940
|
+
action,
|
|
8941
|
+
index,
|
|
8942
|
+
index === failedIndex ? "failed" : "pending",
|
|
8943
|
+
null,
|
|
8944
|
+
index === failedIndex ? err : null,
|
|
8945
|
+
)),
|
|
8946
|
+
};
|
|
8947
|
+
}
|
|
8370
8948
|
attachRuntimeCheckpointCandidates(err, envelope, cwd);
|
|
8371
8949
|
throw err;
|
|
8372
8950
|
}
|
|
8373
8951
|
const results = [];
|
|
8374
8952
|
const skipped = [];
|
|
8953
|
+
const actionLedgerEntries = [];
|
|
8375
8954
|
const actionContext = {
|
|
8376
8955
|
rootIssueId: issueId,
|
|
8377
8956
|
cwd,
|
|
@@ -8385,10 +8964,16 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
|
|
|
8385
8964
|
answeredQuestionContinuation,
|
|
8386
8965
|
finalCommentBodies: [],
|
|
8387
8966
|
};
|
|
8388
|
-
for (const action of actions) {
|
|
8967
|
+
for (const [actionIndex, action] of actions.entries()) {
|
|
8389
8968
|
try {
|
|
8390
8969
|
const result = await applyRuntimeAction(config, command, runtimeAuth, issueId, action, actionContext);
|
|
8391
8970
|
results.push(result);
|
|
8971
|
+
actionLedgerEntries.push(runtimeActionLedgerEntry(
|
|
8972
|
+
action,
|
|
8973
|
+
actionIndex,
|
|
8974
|
+
asRecord(result).reusedExisting === true ? "reused" : "applied",
|
|
8975
|
+
result,
|
|
8976
|
+
));
|
|
8392
8977
|
if (isSkippedRuntimeActionResult(result)) {
|
|
8393
8978
|
skipped.push(result);
|
|
8394
8979
|
}
|
|
@@ -8417,6 +9002,17 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
|
|
|
8417
9002
|
err.runtimeActionOriginalType = readRuntimeActionOriginalType(action);
|
|
8418
9003
|
err.runtimeActionKind = readString(action.kind) ?? readString(asRecord(action.payload).kind);
|
|
8419
9004
|
err.runtimeActionReason = truncateText(err instanceof Error ? err.message : String(err), 1000);
|
|
9005
|
+
err.runtimeActionLedger = {
|
|
9006
|
+
batchFingerprint,
|
|
9007
|
+
traceId: batchFingerprint,
|
|
9008
|
+
status: "partial_failed",
|
|
9009
|
+
actions: [
|
|
9010
|
+
...actionLedgerEntries,
|
|
9011
|
+
runtimeActionLedgerEntry(action, actionIndex, "failed", null, err),
|
|
9012
|
+
...actions.slice(actionIndex + 1).map((pendingAction, offset) =>
|
|
9013
|
+
runtimeActionLedgerEntry(pendingAction, actionIndex + offset + 1, "pending")),
|
|
9014
|
+
],
|
|
9015
|
+
};
|
|
8420
9016
|
}
|
|
8421
9017
|
attachRuntimeCheckpointCandidates(err, envelope, cwd);
|
|
8422
9018
|
throw err;
|
|
@@ -8424,6 +9020,7 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
|
|
|
8424
9020
|
const skippedResult = runtimeActionFailureSummary(action, err);
|
|
8425
9021
|
skipped.push(skippedResult);
|
|
8426
9022
|
results.push(skippedResult);
|
|
9023
|
+
actionLedgerEntries.push(runtimeActionLedgerEntry(action, actionIndex, "skipped", skippedResult, err));
|
|
8427
9024
|
await ingestLog(config, command, "system", "warn", `Skipped AMaster runtime action: ${skippedResult.reason}`, {
|
|
8428
9025
|
runtimeAction: skippedResult,
|
|
8429
9026
|
});
|
|
@@ -8431,8 +9028,19 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
|
|
|
8431
9028
|
}
|
|
8432
9029
|
return {
|
|
8433
9030
|
version: readNumber(envelope.version, 1),
|
|
9031
|
+
markerFound: true,
|
|
9032
|
+
jsonParsed: true,
|
|
9033
|
+
actionsValidated: true,
|
|
9034
|
+
postconditionsPassed: true,
|
|
9035
|
+
actionLedger: {
|
|
9036
|
+
batchFingerprint,
|
|
9037
|
+
traceId: batchFingerprint,
|
|
9038
|
+
status: "completed",
|
|
9039
|
+
actions: actionLedgerEntries,
|
|
9040
|
+
},
|
|
8434
9041
|
requestedCount: actions.length,
|
|
8435
9042
|
appliedCount: results.length - skipped.length,
|
|
9043
|
+
actionsApplied: results.length - skipped.length,
|
|
8436
9044
|
skippedCount: skipped.length,
|
|
8437
9045
|
results,
|
|
8438
9046
|
deliverableManifest: {
|
|
@@ -8488,7 +9096,7 @@ function existingArtifactUploadMatches(artifact, existingWorkProducts) {
|
|
|
8488
9096
|
});
|
|
8489
9097
|
}
|
|
8490
9098
|
|
|
8491
|
-
function existingExplicitArtifactUploadMatch(filePath, title, existingWorkProducts) {
|
|
9099
|
+
function existingExplicitArtifactUploadMatch(filePath, title, existingWorkProducts, fields = {}) {
|
|
8492
9100
|
const filename = basename(filePath);
|
|
8493
9101
|
const byteSize = statSync(filePath).size;
|
|
8494
9102
|
const sha256 = hashFileSha256(filePath);
|
|
@@ -8499,6 +9107,22 @@ function existingExplicitArtifactUploadMatch(filePath, title, existingWorkProduc
|
|
|
8499
9107
|
if (readString(workProduct.provider) !== "paperclip") return false;
|
|
8500
9108
|
if (readString(workProduct.status) === "archived") return false;
|
|
8501
9109
|
const metadata = asRecord(workProduct.metadata);
|
|
9110
|
+
const sourceWorkProductId = readString(fields.sourceWorkProductId);
|
|
9111
|
+
const sourceWorkProductIds = Array.isArray(fields.sourceWorkProductIds)
|
|
9112
|
+
? fields.sourceWorkProductIds.map(readString).filter(Boolean).sort()
|
|
9113
|
+
: [];
|
|
9114
|
+
const supersedesWorkProductId = readString(fields.supersedesWorkProductId);
|
|
9115
|
+
const evidenceRole = readString(fields.evidenceRole);
|
|
9116
|
+
const scenarioKeys = Array.isArray(fields.scenarioKeys) ? fields.scenarioKeys.map(readString).filter(Boolean).sort() : [];
|
|
9117
|
+
if (sourceWorkProductId || supersedesWorkProductId || sourceWorkProductIds.length > 0 || evidenceRole || scenarioKeys.length > 0) {
|
|
9118
|
+
if (readString(metadata.sourceWorkProductId) !== sourceWorkProductId) return false;
|
|
9119
|
+
if (readString(metadata.supersedesWorkProductId) !== supersedesWorkProductId) return false;
|
|
9120
|
+
const existingSourceIds = Array.isArray(metadata.sourceWorkProductIds) ? metadata.sourceWorkProductIds.map(readString).filter(Boolean).sort() : [];
|
|
9121
|
+
const existingScenarioKeys = Array.isArray(metadata.scenarioKeys) ? metadata.scenarioKeys.map(readString).filter(Boolean).sort() : [];
|
|
9122
|
+
if (JSON.stringify(existingSourceIds) !== JSON.stringify(sourceWorkProductIds)) return false;
|
|
9123
|
+
if (readString(metadata.evidenceRole) !== evidenceRole) return false;
|
|
9124
|
+
if (JSON.stringify(existingScenarioKeys) !== JSON.stringify(scenarioKeys)) return false;
|
|
9125
|
+
}
|
|
8502
9126
|
const existingSha = readString(metadata.sha256) ?? readString(workProduct.sha256);
|
|
8503
9127
|
if (existingSha) return existingSha === sha256;
|
|
8504
9128
|
|
|
@@ -9112,6 +9736,26 @@ function statusSummary(config) {
|
|
|
9112
9736
|
}), null, 2)}\n`);
|
|
9113
9737
|
}
|
|
9114
9738
|
|
|
9739
|
+
function validateRuntimeActionsFile(config, flags) {
|
|
9740
|
+
const rawFile = readString(flags.file);
|
|
9741
|
+
if (!rawFile) throw new Error("--file is required");
|
|
9742
|
+
const cwd = resolve(readString(flags.cwd) ?? process.cwd());
|
|
9743
|
+
const file = resolve(cwd, rawFile);
|
|
9744
|
+
const allowedContentTypes = splitList(flags["allowed-content-types"]);
|
|
9745
|
+
const envelope = JSON.parse(readFileSync(file, "utf8"));
|
|
9746
|
+
if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
|
|
9747
|
+
throw runtimeActionValidationError("runtime action envelope must be a JSON object");
|
|
9748
|
+
}
|
|
9749
|
+
assertRuntimeActionValid(envelope.version === 1, "runtime action envelope version must be 1");
|
|
9750
|
+
assertRuntimeActionValid(Array.isArray(envelope.actions), "runtime action envelope actions must be an array");
|
|
9751
|
+
assertRuntimeActionValid(envelope.actions.length <= 20, "runtime action envelope actions exceed the limit of 20");
|
|
9752
|
+
|
|
9753
|
+
preflightRuntimeDeliverables(config, envelope, cwd, allowedContentTypes);
|
|
9754
|
+
const actions = orderRuntimeActionsForGovernance(envelope.actions.map(asRecord));
|
|
9755
|
+
preflightRuntimeActionBatch(actions, { cwd, allowedContentTypes });
|
|
9756
|
+
process.stdout.write(`${JSON.stringify({ valid: true, actionCount: actions.length }, null, 2)}\n`);
|
|
9757
|
+
}
|
|
9758
|
+
|
|
9115
9759
|
async function runLoop(config) {
|
|
9116
9760
|
config = await ensureRegistered(config);
|
|
9117
9761
|
let startupOrphanCheckPending = true;
|
|
@@ -9145,6 +9789,7 @@ Commands:
|
|
|
9145
9789
|
run-once Register if needed, heartbeat, poll once, execute leased commands, then exit.
|
|
9146
9790
|
workspace-gc-dry-run Print managed runtime workdir GC candidates without deleting anything.
|
|
9147
9791
|
status-summary Print safe local daemon storage/outbox summary JSON.
|
|
9792
|
+
validate-actions Validate a JSON action envelope with --file, --cwd, and --allowed-content-types.
|
|
9148
9793
|
start | run Register if needed, then heartbeat and poll in a foreground loop.
|
|
9149
9794
|
|
|
9150
9795
|
Key env:
|
|
@@ -9196,6 +9841,9 @@ async function main() {
|
|
|
9196
9841
|
case "status-summary":
|
|
9197
9842
|
statusSummary(config);
|
|
9198
9843
|
break;
|
|
9844
|
+
case "validate-actions":
|
|
9845
|
+
validateRuntimeActionsFile(config, flags);
|
|
9846
|
+
break;
|
|
9199
9847
|
case "start":
|
|
9200
9848
|
case "run":
|
|
9201
9849
|
await runLoop(config);
|
package/dist/amaster-runtime.mjs
CHANGED
|
@@ -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.
|
|
8
|
+
const CONNECTOR_VERSION = "0.1.0-beta.7";
|
|
9
9
|
|
|
10
10
|
const CAPABILITIES = [
|
|
11
11
|
"remote_registration",
|