@amaster.ai/employee-runtime-connector 0.1.0-beta.11 → 0.1.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/dist/amaster-runtime-daemon.mjs +91 -1177
- package/dist/amaster-runtime.mjs +1 -23
- package/package.json +1 -1
|
@@ -585,23 +585,6 @@ function piRuntimeActionText(message) {
|
|
|
585
585
|
return text.includes("AMASTER_RUNTIME_ACTIONS") ? text : "";
|
|
586
586
|
}
|
|
587
587
|
|
|
588
|
-
function piEventHasRuntimeActionMarkerInToolOutput(event) {
|
|
589
|
-
const directMessage = asRecord(event.message);
|
|
590
|
-
if (directMessage.role === "toolResult" && piRuntimeActionText(directMessage)) return true;
|
|
591
|
-
|
|
592
|
-
const eventMessages = Array.isArray(event.messages) ? event.messages : [];
|
|
593
|
-
if (eventMessages.some((message) => {
|
|
594
|
-
const record = asRecord(message);
|
|
595
|
-
return record.role === "toolResult" && Boolean(piRuntimeActionText(record));
|
|
596
|
-
})) return true;
|
|
597
|
-
|
|
598
|
-
const toolResults = Array.isArray(event.toolResults) ? event.toolResults : [];
|
|
599
|
-
if (toolResults.some((result) => Boolean(piRuntimeActionText(result)))) return true;
|
|
600
|
-
|
|
601
|
-
if (event.type === "tool_execution_end" && piRuntimeActionText(event.result)) return true;
|
|
602
|
-
return false;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
588
|
function appendUniqueText(values, text) {
|
|
606
589
|
const normalized = typeof text === "string" ? text.trim() : "";
|
|
607
590
|
if (!normalized) return false;
|
|
@@ -727,7 +710,6 @@ function parsePiJsonl(stdout) {
|
|
|
727
710
|
let terminalEventType = null;
|
|
728
711
|
let stopReason = null;
|
|
729
712
|
let hasAssistantOutput = false;
|
|
730
|
-
let runtimeActionMarkerInToolOutput = false;
|
|
731
713
|
const messages = [];
|
|
732
714
|
const runtimeActionTexts = [];
|
|
733
715
|
const usage = { inputTokens: 0, cachedInputTokens: 0, outputTokens: 0 };
|
|
@@ -735,8 +717,6 @@ function parsePiJsonl(stdout) {
|
|
|
735
717
|
for (const rawLine of String(stdout ?? "").split(/\r?\n/)) {
|
|
736
718
|
const event = parseJsonLine(rawLine.trim());
|
|
737
719
|
if (!event) continue;
|
|
738
|
-
runtimeActionMarkerInToolOutput = piEventHasRuntimeActionMarkerInToolOutput(event)
|
|
739
|
-
|| runtimeActionMarkerInToolOutput;
|
|
740
720
|
if (event.type === "session") {
|
|
741
721
|
sessionId = readString(event.id) ?? sessionId;
|
|
742
722
|
continue;
|
|
@@ -775,7 +755,6 @@ function parsePiJsonl(stdout) {
|
|
|
775
755
|
sessionId,
|
|
776
756
|
summary: messages.at(-1) ?? "",
|
|
777
757
|
runtimeActionText: runtimeActionTexts.join("\n\n"),
|
|
778
|
-
runtimeActionMarkerInToolOutput,
|
|
779
758
|
usage,
|
|
780
759
|
sawTurnEnd,
|
|
781
760
|
terminalEventType,
|
|
@@ -2331,11 +2310,11 @@ function readWorkspaceStatus(cwd, opts = {}) {
|
|
|
2331
2310
|
// ---- bundled entrypoint: src/amaster-runtime-daemon.mjs ----
|
|
2332
2311
|
|
|
2333
2312
|
import { createHash } from "node:crypto";
|
|
2334
|
-
import {
|
|
2313
|
+
import { copyFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2335
2314
|
import { homedir, hostname } from "node:os";
|
|
2336
2315
|
import { basename, delimiter, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
2337
2316
|
import { spawn, spawnSync } from "node:child_process";
|
|
2338
|
-
const CONNECTOR_VERSION = "0.1.0-beta.
|
|
2317
|
+
const CONNECTOR_VERSION = "0.1.0-beta.2";
|
|
2339
2318
|
const MAX_INFERRED_ARTIFACT_UPLOADS = 20;
|
|
2340
2319
|
const MAX_CHECKPOINT_BYTES = 20 * 1024 * 1024;
|
|
2341
2320
|
const CHECKPOINT_TTL_MS = 24 * 60 * 60 * 1000;
|
|
@@ -3515,31 +3494,11 @@ function canRuntimeAgentOrchestrateOrganization(context) {
|
|
|
3515
3494
|
}
|
|
3516
3495
|
|
|
3517
3496
|
function runtimeActionsInstruction(options = {}) {
|
|
3518
|
-
const authorizationEnvelope = options.authorizationEnvelope ?? {
|
|
3519
|
-
allowedActionClasses: new Set(["task_governance"]),
|
|
3520
|
-
sourceKind: "implicit_default",
|
|
3521
|
-
sourceId: "unknown",
|
|
3522
|
-
sourceRevision: "missing_envelope",
|
|
3523
|
-
};
|
|
3524
|
-
const knownActionClasses = ["task_governance", "organization_mutation", "executable_child"];
|
|
3525
|
-
const allowedActionClasses = knownActionClasses.filter((actionClass) =>
|
|
3526
|
-
authorizationEnvelope.allowedActionClasses.has(actionClass));
|
|
3527
|
-
const deniedActionClasses = knownActionClasses.filter((actionClass) =>
|
|
3528
|
-
!authorizationEnvelope.allowedActionClasses.has(actionClass));
|
|
3529
|
-
const authorizationOptionIdEntries = knownActionClasses
|
|
3530
|
-
.map((actionClass) => [actionClass, authorizationEnvelope.authorizationOptionIds?.get(actionClass)])
|
|
3531
|
-
.filter(([, optionId]) => optionId);
|
|
3532
|
-
const authorizationOptionIdGuidance = authorizationOptionIdEntries.length === knownActionClasses.length
|
|
3533
|
-
? `Authorization checkbox option ids: ${authorizationOptionIdEntries
|
|
3534
|
-
.map(([actionClass, optionId]) => `${actionClass}=${optionId}`)
|
|
3535
|
-
.join(", ")}. Use these exact ids when requesting authorization; do not invent another prefix.`
|
|
3536
|
-
: "Authorization checkbox option ids are unavailable for this wake. Do not invent option ids that grant organization_mutation or executable_child authorization.";
|
|
3537
3497
|
const allowedActionTypes = [
|
|
3538
3498
|
"add_comment",
|
|
3539
3499
|
"update_parent",
|
|
3540
3500
|
"create_child_task",
|
|
3541
3501
|
"upload_artifact",
|
|
3542
|
-
"upsert_document",
|
|
3543
3502
|
...(options.canOrchestrateOrganization ? ["agent_hire"] : []),
|
|
3544
3503
|
"create_interaction",
|
|
3545
3504
|
].join(", ");
|
|
@@ -3547,37 +3506,20 @@ function runtimeActionsInstruction(options = {}) {
|
|
|
3547
3506
|
const artifactVerifierCommands = Array.isArray(options.artifactVerifierCommands)
|
|
3548
3507
|
? options.artifactVerifierCommands.map(readString).filter(Boolean)
|
|
3549
3508
|
: [];
|
|
3550
|
-
const actionValidatorCommand = readString(options.actionValidatorCommand);
|
|
3551
3509
|
return [
|
|
3552
3510
|
"Runtime action bridge:",
|
|
3553
|
-
|
|
3554
|
-
`Denied runtime action classes for this wake: ${deniedActionClasses.join(", ") || "none"}.`,
|
|
3555
|
-
`Wake authorization source: ${authorizationEnvelope.sourceKind}:${authorizationEnvelope.sourceId} revision ${authorizationEnvelope.sourceRevision}.`,
|
|
3556
|
-
authorizationOptionIdGuidance,
|
|
3557
|
-
"This capability guidance is rendered from the same authorization envelope enforced by runtime preflight. Preflight remains the final authority. task_governance covers comments, parent status, backlog children, artifacts, and interactions; organization_mutation covers agent_hire/create_agent; executable_child covers todo/in_progress child creation.",
|
|
3558
|
-
"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.",
|
|
3559
|
-
"If direct AMaster API requests succeed for all requested mutations, finish with a normal summary and do not emit or mention the AMASTER_RUNTIME_ACTIONS marker.",
|
|
3560
|
-
"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.",
|
|
3511
|
+
"If direct AMaster API requests are unavailable from the executor sandbox, do not stop at blocked. Instead finish with this exact marker and a JSON object:",
|
|
3561
3512
|
"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.",
|
|
3562
|
-
"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.",
|
|
3563
|
-
"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.",
|
|
3564
3513
|
"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.",
|
|
3565
3514
|
"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.",
|
|
3566
3515
|
"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.",
|
|
3567
|
-
"Create a child only for an independently executable residual work item. Do not create a nested child merely to prove that child creation works, and do not treat the current issue title as an instruction to recreate the issue itself. If the current child already embodies the requested test or work item, complete it directly.",
|
|
3568
|
-
"Every root create_child_task with status todo or in_progress must establish the parent disposition in the same batch. Set blockParentUntilDone true when the parent depends on that child. Only omit the wait when the same batch also closes the parent's independent scope with update_parent status done or cancelled. parentId alone is structural and never proves that the parent is waiting; missing both paths fails with delegated_parent_wait_required.",
|
|
3569
3516
|
"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.",
|
|
3570
|
-
"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.",
|
|
3571
|
-
"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.",
|
|
3572
|
-
"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.",
|
|
3573
3517
|
options.explicitDecompositionRequired
|
|
3574
|
-
? "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."
|
|
3518
|
+
? "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. If any pending interaction and create_child_task actions share the same batch, every todo or in_progress child in that batch must set executeBeforeConfirmation true. Otherwise change the child to backlog or move it into suggest_tasks. 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."
|
|
3575
3519
|
: "",
|
|
3520
|
+
"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.",
|
|
3576
3521
|
options.canOrchestrateOrganization
|
|
3577
|
-
? "
|
|
3578
|
-
: "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.",
|
|
3579
|
-
options.canOrchestrateOrganization
|
|
3580
|
-
? "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."
|
|
3522
|
+
? "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. If approval is required, create a request_confirmation or leave the hire approval pending. These organization batches must include add_comment or update_parent.comment explaining what was created, requested, assigned, or blocked."
|
|
3581
3523
|
: "",
|
|
3582
3524
|
options.canOrchestrateOrganization
|
|
3583
3525
|
? `For agent_hire, set adapterType explicitly. Default to the current executing agent adapterType${currentAgentAdapterType ? ` (${currentAgentAdapterType})` : ""} unless the task names a different approved adapter.`
|
|
@@ -3585,38 +3527,21 @@ function runtimeActionsInstruction(options = {}) {
|
|
|
3585
3527
|
options.canOrchestrateOrganization
|
|
3586
3528
|
? `For agent_hire, role must be one of: ${AGENT_HIRE_SCHEMA_ROLES.join(", ")}. Put custom business role names in name/title/capabilities/instructionsBundle, not in role. Map product/customer insight to research or product, growth/revenue to marketing or sales, finance/budget to finance, engineering delivery to engineering, legal/compliance to legal, customer success/support to customer_support. capabilities must be a string, not an array. instructionsBundle must use {entryFile:"AGENTS.md", files: {"AGENTS.md": "..."}}, and instructionsBundle.files must contain at least one file.`
|
|
3587
3529
|
: "",
|
|
3588
|
-
options.canOrchestrateOrganization
|
|
3589
|
-
? "Every agent_hire must declare exactly one reporting disposition. For an existing manager from the current task context, set reportsTo to that manager UUID. For a manager hired in the same batch, set reportsToAgentClientKey to the manager's clientKey. Only a genuinely company-root role may set topLevel true. reportsToAgentId is unsupported; use reportsTo for an existing manager UUID."
|
|
3590
|
-
: "",
|
|
3591
3530
|
options.canOrchestrateOrganization
|
|
3592
3531
|
? "When assigning a newly hired agent's first task in the same action batch, set clientKey on the agent_hire action and set assigneeAgentClientKey or agentClientKey on the create_child_task action to that same key. Do not leave the first task assigned to the current CEO/executing agent unless the CEO should actually do the work."
|
|
3593
3532
|
: "",
|
|
3594
|
-
"
|
|
3595
|
-
"
|
|
3596
|
-
"
|
|
3597
|
-
"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.",
|
|
3598
|
-
actionValidatorCommand
|
|
3599
|
-
? `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.`
|
|
3600
|
-
: "",
|
|
3533
|
+
"Output format: write the marker AMASTER_RUNTIME_ACTIONS, then exactly one fenced json object. Use this literal shape with real action objects: {\"version\":1,\"actions\":[{\"type\":\"add_comment\",\"body\":\"Progress or result summary\"}]}.",
|
|
3534
|
+
"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 printing it.",
|
|
3535
|
+
"Before final output, validate the runtime action block with JSON.parse. If it would fail, fix the JSON and only then print AMASTER_RUNTIME_ACTIONS.",
|
|
3601
3536
|
`Allowed action types: ${allowedActionTypes}.`,
|
|
3602
|
-
"For
|
|
3603
|
-
"For
|
|
3604
|
-
"
|
|
3605
|
-
"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.",
|
|
3606
|
-
"For create_interaction request_confirmation, follow the complete example above and do not use options arrays.",
|
|
3607
|
-
"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\":\"调整\",\"target\":{\"type\":\"custom\",\"key\":\"approved-items\",\"label\":\"待批准项目\",\"revisionId\":\"v1\"}}.",
|
|
3537
|
+
"For create_interaction ask_user_questions, 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.",
|
|
3538
|
+
"For create_interaction request_confirmation, use payload {\"version\":1,\"prompt\":\"Confirm this result?\",\"acceptLabel\":\"确认\",\"rejectLabel\":\"调整\"}. Do not use options arrays for request_confirmation.",
|
|
3539
|
+
"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\":\"调整\"}.",
|
|
3608
3540
|
"For create_interaction suggest_tasks, use payload {\"version\":1,\"tasks\":[{\"clientKey\":\"task_key\",\"title\":\"Task title\",\"description\":\"Task description\",\"priority\":\"medium\"}]}.",
|
|
3609
3541
|
"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.",
|
|
3610
|
-
"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.",
|
|
3611
3542
|
"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.",
|
|
3612
3543
|
"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.",
|
|
3613
|
-
"upload_artifact actions do not accept required. Artifact required belongs only to top-level deliverables entries, not action objects.",
|
|
3614
|
-
"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.",
|
|
3615
|
-
"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.",
|
|
3616
|
-
"When a child must consume current parent artifacts, set create_child_task.inputWorkProductIds to the exact parent work product UUIDs from task context. AMaster persists their attachment/SHA revision manifest and materializes only those required inputs under input-artifacts/. Do not assume parent attachments are implicitly copied into every child workspace.",
|
|
3617
|
-
"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.",
|
|
3618
3544
|
"For required deliverables, add top-level deliverables entries using {\"path\":\"relative/file\",\"required\":true}.",
|
|
3619
|
-
"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.",
|
|
3620
3545
|
artifactVerifierCommands.length > 0
|
|
3621
3546
|
? `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.`
|
|
3622
3547
|
: "Executable deliverable verification is disabled by runtime policy. Do not include verification blocks. Required deliverables are still checked for presence and upload completeness.",
|
|
@@ -3904,25 +3829,17 @@ function buildCommandPrompt(command, workspace, materializedAttachments = [], op
|
|
|
3904
3829
|
const agentInstructions = renderAgentInstructionsBundle(asRecord(payload.agentInstructionsBundle));
|
|
3905
3830
|
const runtimeAuth = commandRuntimeAuth(command);
|
|
3906
3831
|
const hasRuntimeApi = Boolean(readString(runtimeAuth.apiUrl) && readString(runtimeAuth.apiKey));
|
|
3907
|
-
const
|
|
3908
|
-
const canOrchestrateOrganization = canRuntimeAgentOrchestrateOrganization(context)
|
|
3909
|
-
&& authorizationEnvelope.allowedActionClasses.has("organization_mutation");
|
|
3832
|
+
const canOrchestrateOrganization = canRuntimeAgentOrchestrateOrganization(context);
|
|
3910
3833
|
const decompositionRequirement = commandLegacyTaskDecompositionRequirement(command);
|
|
3911
3834
|
const explicitDecompositionRequired = decompositionRequirement.required
|
|
3912
3835
|
&& runtimeActionChildIssueSummaryEntries(command).length === 0;
|
|
3913
3836
|
const executingAgent = asRecord(context.paperclipExecutingAgent);
|
|
3914
3837
|
const currentAgentAdapterType = readString(executingAgent.adapterType);
|
|
3915
|
-
const validatorContentTypes = runtimeAttachmentContentTypes(command);
|
|
3916
|
-
// Built-in executors run on the connector host, so the prompt may reference these host paths.
|
|
3917
|
-
// A custom wrapper may isolate model tools; until isolation is an explicit capability, this
|
|
3918
|
-
// validator is same-host only and must fail visibly rather than fall back to a weaker check.
|
|
3919
|
-
const actionValidatorCommand = `${quoteShell(process.execPath)} ${quoteShell(resolve(process.argv[1]))} validate-actions --file .amaster-runtime-actions.json --cwd . --allowed-content-types ${quoteShell(validatorContentTypes.join(","))}`;
|
|
3920
3838
|
return [
|
|
3921
3839
|
"## AMaster Runtime Connector Task",
|
|
3922
3840
|
"",
|
|
3923
3841
|
"You are executing a task dispatched by AMaster Employee from the central control plane.",
|
|
3924
3842
|
"Work only inside the declared workspace. Make concrete progress and finish with a concise result summary.",
|
|
3925
|
-
"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.",
|
|
3926
3843
|
"",
|
|
3927
3844
|
`- command id: ${command.commandId}`,
|
|
3928
3845
|
`- run id: ${commandRunId(command) ?? "unknown"}`,
|
|
@@ -3930,9 +3847,7 @@ function buildCommandPrompt(command, workspace, materializedAttachments = [], op
|
|
|
3930
3847
|
issueLine ? `- issue: ${issueLine}` : "",
|
|
3931
3848
|
`- workspace: ${cwd}`,
|
|
3932
3849
|
workspaceContext.managed ? `- source workspace: ${workspaceContext.sourceWorkspacePath}` : "",
|
|
3933
|
-
workspaceContext.managed
|
|
3934
|
-
? "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."
|
|
3935
|
-
: "",
|
|
3850
|
+
workspaceContext.managed ? "Use the execution workspace as cwd. Do not run broad scans in the source workspace; only inspect specific source paths when the task requires it." : "",
|
|
3936
3851
|
readString(context.wakeReason) ? `- wake reason: ${readString(context.wakeReason)}` : "",
|
|
3937
3852
|
hasRuntimeApi
|
|
3938
3853
|
? "- 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."
|
|
@@ -3943,8 +3858,6 @@ function buildCommandPrompt(command, workspace, materializedAttachments = [], op
|
|
|
3943
3858
|
currentAgentAdapterType,
|
|
3944
3859
|
explicitDecompositionRequired,
|
|
3945
3860
|
artifactVerifierCommands: options.artifactVerifierCommands,
|
|
3946
|
-
actionValidatorCommand,
|
|
3947
|
-
authorizationEnvelope,
|
|
3948
3861
|
}) : "",
|
|
3949
3862
|
"",
|
|
3950
3863
|
agentInstructions,
|
|
@@ -5356,10 +5269,9 @@ function extractRuntimeActionEnvelope(text) {
|
|
|
5356
5269
|
const structuredEnvelope = tryExtractStructuredRuntimeActionEnvelope(text);
|
|
5357
5270
|
if (structuredEnvelope) return structuredEnvelope;
|
|
5358
5271
|
|
|
5359
|
-
const
|
|
5360
|
-
if (
|
|
5361
|
-
const
|
|
5362
|
-
const jsonStart = text.indexOf("{", markerEnd);
|
|
5272
|
+
const markerIndex = text.indexOf("AMASTER_RUNTIME_ACTIONS");
|
|
5273
|
+
if (markerIndex === -1) return null;
|
|
5274
|
+
const jsonStart = text.indexOf("{", markerIndex);
|
|
5363
5275
|
if (jsonStart === -1) {
|
|
5364
5276
|
const err = new Error("AMASTER_RUNTIME_ACTIONS JSON could not be parsed: missing JSON object");
|
|
5365
5277
|
err.runtimeActionType = "parse";
|
|
@@ -5378,9 +5290,7 @@ function extractRuntimeActionEnvelope(text) {
|
|
|
5378
5290
|
|
|
5379
5291
|
function tryExtractStructuredRuntimeActionEnvelope(text) {
|
|
5380
5292
|
const source = String(text ?? "");
|
|
5381
|
-
|
|
5382
|
-
// marker itself must still be the only content on its line.
|
|
5383
|
-
const markerRe = /(^|\r?\n)[ \t]*AMASTER_RUNTIME_ACTIONS[ \t]*(?:\r?\n|$)/g;
|
|
5293
|
+
const markerRe = /(^|\r?\n)[ \t]*AMASTER_RUNTIME_ACTIONS\b/g;
|
|
5384
5294
|
let match;
|
|
5385
5295
|
while ((match = markerRe.exec(source)) !== null) {
|
|
5386
5296
|
let cursor = markerRe.lastIndex;
|
|
@@ -5420,15 +5330,6 @@ function parseRuntimeActionEnvelopeJson(jsonText) {
|
|
|
5420
5330
|
}
|
|
5421
5331
|
}
|
|
5422
5332
|
|
|
5423
|
-
function hasMalformedRuntimeActionMarkerIntent(text) {
|
|
5424
|
-
const value = String(text ?? "");
|
|
5425
|
-
if (!value.includes("AMASTER_RUNTIME_ACTIONS")) return false;
|
|
5426
|
-
const hasNonCanonicalMarkerLine = value
|
|
5427
|
-
.split(/\r?\n/)
|
|
5428
|
-
.some((line) => line.includes("AMASTER_RUNTIME_ACTIONS") && line.trim() !== "AMASTER_RUNTIME_ACTIONS");
|
|
5429
|
-
return hasNonCanonicalMarkerLine && /["']actions["']\s*:/.test(value);
|
|
5430
|
-
}
|
|
5431
|
-
|
|
5432
5333
|
function readBoundedActionString(record, key, maxChars) {
|
|
5433
5334
|
const value = readString(record[key]);
|
|
5434
5335
|
return value ? truncateText(value, maxChars) : null;
|
|
@@ -5792,97 +5693,6 @@ async function materializeIssueAttachments(config, command, workspace) {
|
|
|
5792
5693
|
return materialized;
|
|
5793
5694
|
}
|
|
5794
5695
|
|
|
5795
|
-
function safeArtifactInputSourceDir(entry, index) {
|
|
5796
|
-
const raw = readString(entry.sourceIssueIdentifier) ?? readString(entry.sourceIssueId) ?? `source-${index + 1}`;
|
|
5797
|
-
const safe = raw.replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^\.+|\.+$/g, "").slice(0, 120);
|
|
5798
|
-
return safe && safe !== "." && safe !== ".." ? safe : `source-${index + 1}`;
|
|
5799
|
-
}
|
|
5800
|
-
|
|
5801
|
-
async function materializeRequiredArtifactInputs(config, command, workspace) {
|
|
5802
|
-
const runtimeAuth = commandRuntimeAuth(command);
|
|
5803
|
-
if (!readString(runtimeAuth.apiUrl) || !readString(runtimeAuth.apiKey)) return [];
|
|
5804
|
-
const context = asRecord(asRecord(command.payload).contextSnapshot);
|
|
5805
|
-
const issue = asRecord(context.paperclipIssue);
|
|
5806
|
-
const manifest = asRecord(issue.artifactInputManifest);
|
|
5807
|
-
if (Object.keys(manifest).length === 0) return [];
|
|
5808
|
-
if (manifest.version !== 1 || !Array.isArray(manifest.entries)) {
|
|
5809
|
-
throw new Error("artifact_input_manifest_invalid: expected version 1 entries array");
|
|
5810
|
-
}
|
|
5811
|
-
|
|
5812
|
-
const targetRoot = join(workspace.cwd, "input-artifacts");
|
|
5813
|
-
rmSync(targetRoot, { recursive: true, force: true });
|
|
5814
|
-
mkdirSync(targetRoot, { recursive: true });
|
|
5815
|
-
const usedPaths = new Set();
|
|
5816
|
-
const materialized = [];
|
|
5817
|
-
for (const [index, rawEntry] of manifest.entries.entries()) {
|
|
5818
|
-
const entry = asRecord(rawEntry);
|
|
5819
|
-
const workProductId = readString(entry.workProductId);
|
|
5820
|
-
const attachmentId = readString(entry.attachmentId);
|
|
5821
|
-
const sha256 = readString(entry.sha256);
|
|
5822
|
-
const contentPath = readString(entry.contentPath);
|
|
5823
|
-
const byteSize = readNumber(entry.byteSize, null);
|
|
5824
|
-
if (!workProductId || !attachmentId || !/^[a-f0-9]{64}$/i.test(sha256 ?? "") || !contentPath || byteSize === null) {
|
|
5825
|
-
throw new Error(`artifact_input_manifest_invalid: entry ${index} is incomplete`);
|
|
5826
|
-
}
|
|
5827
|
-
const expectedContentPath = `/api/attachments/${attachmentId}/content`;
|
|
5828
|
-
if (contentPath !== expectedContentPath) {
|
|
5829
|
-
throw new Error(`artifact_input_manifest_invalid: entry ${index} contentPath does not match attachmentId`);
|
|
5830
|
-
}
|
|
5831
|
-
const body = await runtimeApiBuffer(runtimeAuth, contentPath);
|
|
5832
|
-
const actualSha256 = createHash("sha256").update(body).digest("hex");
|
|
5833
|
-
if (body.byteLength !== byteSize || actualSha256 !== sha256) {
|
|
5834
|
-
throw new Error(
|
|
5835
|
-
`artifact_input_integrity_mismatch: workProductId=${workProductId} expectedBytes=${byteSize} actualBytes=${body.byteLength} expectedSha256=${sha256} actualSha256=${actualSha256}`,
|
|
5836
|
-
);
|
|
5837
|
-
}
|
|
5838
|
-
|
|
5839
|
-
const sourceDir = safeArtifactInputSourceDir(entry, index);
|
|
5840
|
-
let filename = safeMaterializedAttachmentFilename({
|
|
5841
|
-
id: attachmentId,
|
|
5842
|
-
originalFilename: readString(entry.originalFilename),
|
|
5843
|
-
}, index);
|
|
5844
|
-
let relativePath = join("input-artifacts", sourceDir, filename).split(/[\\/]+/).join("/");
|
|
5845
|
-
if (usedPaths.has(relativePath)) {
|
|
5846
|
-
const ext = extname(filename);
|
|
5847
|
-
const stem = ext ? filename.slice(0, -ext.length) : filename;
|
|
5848
|
-
filename = `${stem}-${index + 1}${ext}`;
|
|
5849
|
-
relativePath = join("input-artifacts", sourceDir, filename).split(/[\\/]+/).join("/");
|
|
5850
|
-
}
|
|
5851
|
-
usedPaths.add(relativePath);
|
|
5852
|
-
const targetPath = join(workspace.cwd, relativePath);
|
|
5853
|
-
mkdirSync(dirname(targetPath), { recursive: true });
|
|
5854
|
-
writeFileSync(targetPath, body);
|
|
5855
|
-
chmodSync(targetPath, 0o444);
|
|
5856
|
-
materialized.push({
|
|
5857
|
-
id: attachmentId,
|
|
5858
|
-
workProductId,
|
|
5859
|
-
sourceIssueId: readString(entry.sourceIssueId),
|
|
5860
|
-
sourceIssueIdentifier: readString(entry.sourceIssueIdentifier),
|
|
5861
|
-
name: readString(entry.originalFilename) ?? filename,
|
|
5862
|
-
path: targetPath,
|
|
5863
|
-
relativePath,
|
|
5864
|
-
contentType: readString(entry.contentType),
|
|
5865
|
-
byteSize: body.byteLength,
|
|
5866
|
-
sha256,
|
|
5867
|
-
contentPath,
|
|
5868
|
-
});
|
|
5869
|
-
}
|
|
5870
|
-
|
|
5871
|
-
const localManifest = {
|
|
5872
|
-
version: 1,
|
|
5873
|
-
entries: materialized.map(({ path: _path, relativePath, ...entry }) => ({ ...entry, path: relativePath })),
|
|
5874
|
-
};
|
|
5875
|
-
const manifestPath = join(targetRoot, "artifact-input-manifest.json");
|
|
5876
|
-
writeFileSync(manifestPath, JSON.stringify(localManifest, null, 2) + "\n");
|
|
5877
|
-
chmodSync(manifestPath, 0o444);
|
|
5878
|
-
updateWorkspaceManifest(workspaceManifestPath(workspace), { artifactInputManifest: localManifest });
|
|
5879
|
-
await ingestLog(config, command, "system", "info", `Materialized ${materialized.length} required artifact input(s) into the execution workspace`, {
|
|
5880
|
-
artifactInputCount: materialized.length,
|
|
5881
|
-
artifactInputs: localManifest.entries,
|
|
5882
|
-
});
|
|
5883
|
-
return materialized;
|
|
5884
|
-
}
|
|
5885
|
-
|
|
5886
5696
|
function issueCheckpointDir(workspace) {
|
|
5887
5697
|
return join(dirname(workspace.runDir), "checkpoint");
|
|
5888
5698
|
}
|
|
@@ -6182,14 +5992,7 @@ function runtimeChildActionFingerprint(action, issueId, parentIssueId) {
|
|
|
6182
5992
|
title,
|
|
6183
5993
|
status: readActionStatus(action.status) ?? null,
|
|
6184
5994
|
priority: readActionPriority(action.priority) ?? null,
|
|
6185
|
-
blockParentUntilDone: action.blockParentUntilDone === true || readActionStatus(action.status) === "blocked",
|
|
6186
5995
|
parentClientKey: readString(action.parentClientKey) ?? null,
|
|
6187
|
-
inputWorkProductIds: Array.isArray(action.inputWorkProductIds)
|
|
6188
|
-
? action.inputWorkProductIds.map(readString).filter(Boolean).sort()
|
|
6189
|
-
: [],
|
|
6190
|
-
blockedByClientKeys: Array.isArray(action.blockedByClientKeys)
|
|
6191
|
-
? action.blockedByClientKeys.map(readString).filter(Boolean).sort()
|
|
6192
|
-
: [],
|
|
6193
5996
|
})}`;
|
|
6194
5997
|
}
|
|
6195
5998
|
|
|
@@ -6207,9 +6010,6 @@ function runtimeAgentHireActionFingerprint(action, companyId, issueId, body) {
|
|
|
6207
6010
|
adapterType: readString(body.adapterType) ?? null,
|
|
6208
6011
|
sourceIssueId: readString(body.sourceIssueId) ?? null,
|
|
6209
6012
|
sourceIssueIds: runtimeActionIssueIdList(body.sourceIssueIds).sort(),
|
|
6210
|
-
reportsTo: readString(body.reportsTo) ?? null,
|
|
6211
|
-
reportsToAgentClientKey: readString(action.reportsToAgentClientKey) ?? null,
|
|
6212
|
-
topLevel: action.topLevel === true,
|
|
6213
6013
|
})}`;
|
|
6214
6014
|
}
|
|
6215
6015
|
|
|
@@ -6257,6 +6057,21 @@ function runtimeActionDoneNeedsExternalHuman(action, actionContext) {
|
|
|
6257
6057
|
].some((pattern) => pattern.test(normalized));
|
|
6258
6058
|
}
|
|
6259
6059
|
|
|
6060
|
+
function runtimeActionExternalHumanPatch(comment) {
|
|
6061
|
+
const owner = "人工负责人";
|
|
6062
|
+
const action = "补充或授权外部输入后再继续;涉及个人信息、付款、报名提交或审批时由人工处理";
|
|
6063
|
+
const guard = [
|
|
6064
|
+
"AMaster 已检测到本次运行仍依赖人工授权或外部输入,任务已转为 blocked / external_owner_action。本次运行不会自动发送飞书消息;请通过人工协作或已配置的飞书 CLI 通知对应负责人处理,收到明确授权后再继续。",
|
|
6065
|
+
"",
|
|
6066
|
+
`external owner: ${owner}`,
|
|
6067
|
+
`external action: ${action}`,
|
|
6068
|
+
].join("\n");
|
|
6069
|
+
return {
|
|
6070
|
+
status: "blocked",
|
|
6071
|
+
comment: comment ? `${comment}\n\n${guard}` : guard,
|
|
6072
|
+
};
|
|
6073
|
+
}
|
|
6074
|
+
|
|
6260
6075
|
function runtimeActionReviewConfirmation(action, runtimeAuth, command, actionContext, opts = {}) {
|
|
6261
6076
|
const runId = runtimeActionRunId(runtimeAuth) ?? commandRunId(command) ?? "unknown-run";
|
|
6262
6077
|
const comment = readBoundedActionString(action, "comment", 20_000)
|
|
@@ -6301,13 +6116,8 @@ function runtimeActionReviewConfirmation(action, runtimeAuth, command, actionCon
|
|
|
6301
6116
|
payload: {
|
|
6302
6117
|
version: 1,
|
|
6303
6118
|
prompt: copy.prompt,
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
requestChangesLabel: "退回修改",
|
|
6307
|
-
requestChangesReasonLabel: "说明需要补充或修改的内容",
|
|
6308
|
-
rejectLabel: "拒绝并停止",
|
|
6309
|
-
rejectRequiresReason: true,
|
|
6310
|
-
rejectReasonLabel: "说明停止该任务的原因",
|
|
6119
|
+
acceptLabel: "确认,继续推进",
|
|
6120
|
+
rejectLabel: "需要修订",
|
|
6311
6121
|
detailsMarkdown,
|
|
6312
6122
|
target: {
|
|
6313
6123
|
type: "custom",
|
|
@@ -6367,7 +6177,7 @@ function runtimeActionAcceptedConfirmationContinuation(command) {
|
|
|
6367
6177
|
if (context.acceptedConfirmationContinuation === true) return true;
|
|
6368
6178
|
if (readString(context.wakeReason) === "request_confirmation_accepted") return true;
|
|
6369
6179
|
if (
|
|
6370
|
-
|
|
6180
|
+
readString(context.interactionKind) === "request_confirmation" &&
|
|
6371
6181
|
readString(context.interactionStatus) === "accepted"
|
|
6372
6182
|
) {
|
|
6373
6183
|
return true;
|
|
@@ -6376,7 +6186,7 @@ function runtimeActionAcceptedConfirmationContinuation(command) {
|
|
|
6376
6186
|
const interactions = Array.isArray(wake.interactions) ? wake.interactions : [];
|
|
6377
6187
|
return interactions.some((entry) => {
|
|
6378
6188
|
const interaction = asRecord(entry);
|
|
6379
|
-
return
|
|
6189
|
+
return readString(interaction.kind) === "request_confirmation" &&
|
|
6380
6190
|
readString(interaction.status) === "accepted";
|
|
6381
6191
|
});
|
|
6382
6192
|
}
|
|
@@ -6406,31 +6216,6 @@ function runtimeActionContinuationScope(command) {
|
|
|
6406
6216
|
return ["revision_only", "interaction_accepted", "message_only"].includes(scope ?? "") ? scope : null;
|
|
6407
6217
|
}
|
|
6408
6218
|
|
|
6409
|
-
function runtimeActionAuthorizationEnvelope(command) {
|
|
6410
|
-
const payload = asRecord(command.payload);
|
|
6411
|
-
const context = asRecord(payload.contextSnapshot);
|
|
6412
|
-
const rawEnvelope = asRecord(context.authorizationEnvelope);
|
|
6413
|
-
const allowedActionClasses = Array.isArray(rawEnvelope.allowedActionClasses)
|
|
6414
|
-
? rawEnvelope.allowedActionClasses.map(readString).filter(Boolean)
|
|
6415
|
-
: [];
|
|
6416
|
-
const effectiveAllowedActionClasses = allowedActionClasses.length > 0
|
|
6417
|
-
? allowedActionClasses
|
|
6418
|
-
: ["task_governance"];
|
|
6419
|
-
const rawAuthorizationOptionIds = asRecord(rawEnvelope.authorizationOptionIds);
|
|
6420
|
-
const authorizationOptionIds = new Map(
|
|
6421
|
-
["task_governance", "organization_mutation", "executable_child"]
|
|
6422
|
-
.map((actionClass) => [actionClass, readString(rawAuthorizationOptionIds[actionClass])])
|
|
6423
|
-
.filter(([, optionId]) => optionId),
|
|
6424
|
-
);
|
|
6425
|
-
return {
|
|
6426
|
-
allowedActionClasses: new Set(effectiveAllowedActionClasses),
|
|
6427
|
-
authorizationOptionIds,
|
|
6428
|
-
sourceKind: readString(rawEnvelope.sourceKind) ?? "implicit_default",
|
|
6429
|
-
sourceId: readString(rawEnvelope.sourceId) ?? commandRunId(command),
|
|
6430
|
-
sourceRevision: readString(rawEnvelope.sourceRevision) ?? "missing_envelope",
|
|
6431
|
-
};
|
|
6432
|
-
}
|
|
6433
|
-
|
|
6434
6219
|
function runtimeActionInReviewNeedsFollowUpText(text) {
|
|
6435
6220
|
const normalized = String(text ?? "")
|
|
6436
6221
|
.replace(/\s+/g, "")
|
|
@@ -6593,35 +6378,6 @@ function runtimeActionParentIssueId(action, actionContext, fallbackIssueId) {
|
|
|
6593
6378
|
return parentIssueId;
|
|
6594
6379
|
}
|
|
6595
6380
|
|
|
6596
|
-
function runtimeActionReportsToAgentId(action, actionContext) {
|
|
6597
|
-
const reportsToAgentClientKey = readString(action.reportsToAgentClientKey);
|
|
6598
|
-
if (!reportsToAgentClientKey) return readString(action.reportsTo);
|
|
6599
|
-
const reportsTo = actionContext.agentIdByClientKey.get(reportsToAgentClientKey);
|
|
6600
|
-
if (!reportsTo) {
|
|
6601
|
-
throw new Error(`agent_hire action references unknown reportsToAgentClientKey: ${reportsToAgentClientKey}`);
|
|
6602
|
-
}
|
|
6603
|
-
return reportsTo;
|
|
6604
|
-
}
|
|
6605
|
-
|
|
6606
|
-
function runtimeActionBlockedByIssueIds(action, actionContext) {
|
|
6607
|
-
const explicitIssueIds = Array.isArray(action.blockedByIssueIds)
|
|
6608
|
-
? action.blockedByIssueIds.map((rawIssueId) => readString(rawIssueId)).filter(Boolean)
|
|
6609
|
-
: [];
|
|
6610
|
-
if (action.blockedByClientKeys === undefined) return [...new Set(explicitIssueIds)];
|
|
6611
|
-
const clientKeyIssueIds = action.blockedByClientKeys.map((rawClientKey, index) => {
|
|
6612
|
-
const clientKey = readString(rawClientKey);
|
|
6613
|
-
if (!clientKey) {
|
|
6614
|
-
throw runtimeActionValidationError(`create_child_task blockedByClientKeys[${index}] must be a non-empty string`);
|
|
6615
|
-
}
|
|
6616
|
-
const blockerIssueId = actionContext.issueIdByClientKey.get(clientKey);
|
|
6617
|
-
if (!blockerIssueId) {
|
|
6618
|
-
throw new Error(`create_child_task action references unknown blockedByClientKeys entry: ${clientKey}`);
|
|
6619
|
-
}
|
|
6620
|
-
return blockerIssueId;
|
|
6621
|
-
});
|
|
6622
|
-
return [...new Set([...explicitIssueIds, ...clientKeyIssueIds])];
|
|
6623
|
-
}
|
|
6624
|
-
|
|
6625
6381
|
const RUNTIME_EXECUTOR_ADAPTER_TYPE_ALIASES = new Map([
|
|
6626
6382
|
["pi", "pi_local"],
|
|
6627
6383
|
["pi_local", "pi_local"],
|
|
@@ -6843,14 +6599,9 @@ function normalizeRequestConfirmationPayload(payload, action) {
|
|
|
6843
6599
|
return {
|
|
6844
6600
|
version: normalized.version === 1 ? normalized.version : 1,
|
|
6845
6601
|
prompt,
|
|
6846
|
-
...(readString(normalized.resolutionMode) === "review" ? { resolutionMode: "review" } : {}),
|
|
6847
6602
|
...(readString(normalized.acceptLabel) ?? readString(action.acceptLabel)
|
|
6848
6603
|
? { acceptLabel: readString(normalized.acceptLabel) ?? readString(action.acceptLabel) }
|
|
6849
6604
|
: {}),
|
|
6850
|
-
...(readString(normalized.requestChangesLabel) ? { requestChangesLabel: readString(normalized.requestChangesLabel) } : {}),
|
|
6851
|
-
...(readString(normalized.requestChangesReasonLabel)
|
|
6852
|
-
? { requestChangesReasonLabel: readString(normalized.requestChangesReasonLabel) }
|
|
6853
|
-
: {}),
|
|
6854
6605
|
...(readString(normalized.rejectLabel) ?? readString(action.rejectLabel)
|
|
6855
6606
|
? { rejectLabel: readString(normalized.rejectLabel) ?? readString(action.rejectLabel) }
|
|
6856
6607
|
: {}),
|
|
@@ -7069,245 +6820,6 @@ function assertRuntimeActionValid(condition, message) {
|
|
|
7069
6820
|
if (!condition) throw runtimeActionValidationError(message);
|
|
7070
6821
|
}
|
|
7071
6822
|
|
|
7072
|
-
const RUNTIME_ACTION_COMMON_FIELDS = new Set(["type", "action", "name", "payload"]);
|
|
7073
|
-
const RUNTIME_ACTION_ALLOWED_FIELDS = new Map([
|
|
7074
|
-
["create_child_task", new Set([
|
|
7075
|
-
"title", "description", "status", "priority", "assigneeAgentId", "assigneeUserId",
|
|
7076
|
-
"assigneeAgentClientKey", "agentClientKey", "blockParentUntilDone", "executeBeforeConfirmation",
|
|
7077
|
-
"clientKey", "idempotencyKey", "parentClientKey", "blockedByIssueIds", "blockedByClientKeys", "create_child_task",
|
|
7078
|
-
"inputWorkProductIds",
|
|
7079
|
-
])],
|
|
7080
|
-
["agent_hire", new Set([
|
|
7081
|
-
"clientKey", "idempotencyKey", "key", "companyId", "role", "title", "icon", "reportsTo",
|
|
7082
|
-
"reportsToAgentClientKey", "topLevel", "capabilities", "desiredSkills", "adapterType", "adapterConfig",
|
|
7083
|
-
"instructionsBundle", "runtimeConfig", "defaultEnvironmentId", "budgetMonthlyCents", "permissions",
|
|
7084
|
-
"metadata", "sourceIssueId", "sourceIssueIds", "executeBeforeConfirmation", "agent_hire", "agent_hire/create_agent",
|
|
7085
|
-
])],
|
|
7086
|
-
["create_agent", new Set([
|
|
7087
|
-
"clientKey", "idempotencyKey", "key", "companyId", "role", "title", "icon", "reportsTo",
|
|
7088
|
-
"reportsToAgentClientKey", "topLevel", "capabilities", "desiredSkills", "adapterType", "adapterConfig",
|
|
7089
|
-
"instructionsBundle", "runtimeConfig", "defaultEnvironmentId", "budgetMonthlyCents", "permissions",
|
|
7090
|
-
"metadata", "sourceIssueId", "sourceIssueIds", "executeBeforeConfirmation", "create_agent",
|
|
7091
|
-
])],
|
|
7092
|
-
["add_comment", new Set(["body", "content", "text", "message", "add_comment"])],
|
|
7093
|
-
["upload_artifact", new Set([
|
|
7094
|
-
"path", "filePath", "artifactPath", "artifact_path", "title", "summary", "contentType",
|
|
7095
|
-
"createWorkProduct", "provider", "status", "reviewState", "isPrimary", "healthStatus",
|
|
7096
|
-
"sourceWorkProductId", "sourceWorkProductIds", "supersedesWorkProductId", "evidenceRole", "scenarioKeys",
|
|
7097
|
-
"idempotencyKey", "upload_artifact",
|
|
7098
|
-
])],
|
|
7099
|
-
["upsert_document", new Set([
|
|
7100
|
-
"key", "title", "body", "format", "changeSummary", "baseRevisionId", "upsert_document",
|
|
7101
|
-
])],
|
|
7102
|
-
["create_interaction", new Set([
|
|
7103
|
-
"kind", "continuationPolicy", "title", "summary", "idempotencyKey", "interaction", "version",
|
|
7104
|
-
"prompt", "question", "body", "content", "text", "message", "description", "acceptLabel",
|
|
7105
|
-
"requestChangesLabel", "requestChangesReasonLabel", "rejectLabel", "rejectRequiresReason",
|
|
7106
|
-
"rejectReasonLabel", "allowDeclineReason", "declineReasonPlaceholder", "detailsMarkdown",
|
|
7107
|
-
"supersedeOnUserComment", "target", "artifactRefs", "tasks", "options", "defaultSelectedOptionIds",
|
|
7108
|
-
"minSelected", "maxSelected", "questions", "submitLabel", "selectionMode", "required", "helpText",
|
|
7109
|
-
"create_interaction",
|
|
7110
|
-
])],
|
|
7111
|
-
["update_parent", new Set(["status", "comment", "force", "update_parent"])],
|
|
7112
|
-
["update_company_profile", new Set([
|
|
7113
|
-
"companyId", "tagline", "stage", "targetCustomers", "products", "currentPriority", "businessModel",
|
|
7114
|
-
"regions", "brandTone", "markAutoDerived", "idempotencyKey", "update_company_profile",
|
|
7115
|
-
])],
|
|
7116
|
-
["create_routine", new Set([
|
|
7117
|
-
"companyId", "title", "description", "cronExpression", "priority", "concurrencyPolicy", "catchUpPolicy",
|
|
7118
|
-
"assigneeAgentId", "timezone", "idempotencyKey", "create_routine",
|
|
7119
|
-
])],
|
|
7120
|
-
]);
|
|
7121
|
-
|
|
7122
|
-
function runtimeActionReceivedType(value) {
|
|
7123
|
-
if (value === null) return "null";
|
|
7124
|
-
if (Array.isArray(value)) return "array";
|
|
7125
|
-
return typeof value;
|
|
7126
|
-
}
|
|
7127
|
-
|
|
7128
|
-
function assertNoUnsupportedRuntimeActionFields(action, actionIndex) {
|
|
7129
|
-
const type = readRuntimeActionType(action);
|
|
7130
|
-
const allowed = RUNTIME_ACTION_ALLOWED_FIELDS.get(type);
|
|
7131
|
-
assertRuntimeActionValid(allowed, `unsupported AMaster runtime action type: ${type ?? "missing"}`);
|
|
7132
|
-
const fields = runtimeActionBusinessFields(action);
|
|
7133
|
-
for (const [field, value] of Object.entries(fields)) {
|
|
7134
|
-
if (type === "create_interaction" && field === "interactionType") continue;
|
|
7135
|
-
if (RUNTIME_ACTION_COMMON_FIELDS.has(field) || allowed.has(field)) continue;
|
|
7136
|
-
const fieldPath = `actions[${actionIndex}].${field}`;
|
|
7137
|
-
const receivedType = runtimeActionReceivedType(value);
|
|
7138
|
-
const managerFieldHint = ["agent_hire", "create_agent"].includes(type ?? "") && field === "reportsToAgentId"
|
|
7139
|
-
? "; reportsToAgentId is unsupported; use reportsTo for an existing manager UUID or reportsToAgentClientKey for a manager hired in the same batch"
|
|
7140
|
-
: "";
|
|
7141
|
-
const err = runtimeActionValidationError(
|
|
7142
|
-
`${type} action has unsupported field ${field} at ${fieldPath}; received ${receivedType}${managerFieldHint}`,
|
|
7143
|
-
);
|
|
7144
|
-
err.runtimeActionFieldPath = fieldPath;
|
|
7145
|
-
err.runtimeActionReceivedType = receivedType;
|
|
7146
|
-
throw err;
|
|
7147
|
-
}
|
|
7148
|
-
}
|
|
7149
|
-
|
|
7150
|
-
const RUNTIME_ACTION_BOOLEAN_FIELDS = new Set([
|
|
7151
|
-
"blockParentUntilDone", "executeBeforeConfirmation", "createWorkProduct", "isPrimary",
|
|
7152
|
-
"force", "markAutoDerived", "rejectRequiresReason", "allowDeclineReason", "supersedeOnUserComment",
|
|
7153
|
-
"required", "topLevel",
|
|
7154
|
-
]);
|
|
7155
|
-
const RUNTIME_ACTION_NUMBER_FIELDS = new Set(["budgetMonthlyCents", "minSelected", "maxSelected", "version"]);
|
|
7156
|
-
const RUNTIME_ACTION_ARRAY_FIELDS = new Set([
|
|
7157
|
-
"blockedByIssueIds", "blockedByClientKeys", "desiredSkills", "sourceIssueIds", "artifactRefs", "tasks",
|
|
7158
|
-
"options", "defaultSelectedOptionIds", "questions", "sourceWorkProductIds", "scenarioKeys",
|
|
7159
|
-
"inputWorkProductIds",
|
|
7160
|
-
]);
|
|
7161
|
-
const RUNTIME_ACTION_OBJECT_FIELDS = new Set([
|
|
7162
|
-
"adapterConfig", "instructionsBundle", "runtimeConfig", "permissions", "metadata", "target",
|
|
7163
|
-
"interaction",
|
|
7164
|
-
]);
|
|
7165
|
-
|
|
7166
|
-
function assertRuntimeActionFieldTypes(action, actionIndex) {
|
|
7167
|
-
const fields = runtimeActionBusinessFields(action);
|
|
7168
|
-
const type = readRuntimeActionType(fields) ?? "unknown";
|
|
7169
|
-
for (const [field, value] of Object.entries(fields)) {
|
|
7170
|
-
if (value === undefined) continue;
|
|
7171
|
-
if (SINGLE_KEY_RUNTIME_ACTION_TYPES.has(field)) continue;
|
|
7172
|
-
if (type === "create_interaction" && field === "payload") continue;
|
|
7173
|
-
let expectedType = null;
|
|
7174
|
-
let valid = true;
|
|
7175
|
-
if (RUNTIME_ACTION_BOOLEAN_FIELDS.has(field)) {
|
|
7176
|
-
expectedType = "boolean";
|
|
7177
|
-
valid = typeof value === "boolean";
|
|
7178
|
-
} else if (RUNTIME_ACTION_NUMBER_FIELDS.has(field)) {
|
|
7179
|
-
expectedType = "number";
|
|
7180
|
-
valid = typeof value === "number" && Number.isFinite(value);
|
|
7181
|
-
} else if (RUNTIME_ACTION_ARRAY_FIELDS.has(field)) {
|
|
7182
|
-
expectedType = "array";
|
|
7183
|
-
valid = Array.isArray(value);
|
|
7184
|
-
} else if (field === "capabilities") {
|
|
7185
|
-
expectedType = "string or array";
|
|
7186
|
-
valid = typeof value === "string" || Array.isArray(value);
|
|
7187
|
-
} else if (RUNTIME_ACTION_OBJECT_FIELDS.has(field) || field === "payload") {
|
|
7188
|
-
expectedType = "object";
|
|
7189
|
-
valid = value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7190
|
-
} else if (!RUNTIME_ACTION_COMMON_FIELDS.has(field) && value !== null) {
|
|
7191
|
-
expectedType = "string";
|
|
7192
|
-
valid = typeof value === "string";
|
|
7193
|
-
}
|
|
7194
|
-
if (valid || !expectedType) continue;
|
|
7195
|
-
const fieldPath = `actions[${actionIndex}].${field}`;
|
|
7196
|
-
const err = runtimeActionValidationError(
|
|
7197
|
-
`${type} action field ${fieldPath} must be ${expectedType}; received ${runtimeActionReceivedType(value)}`,
|
|
7198
|
-
);
|
|
7199
|
-
err.runtimeActionFieldPath = fieldPath;
|
|
7200
|
-
err.runtimeActionReceivedType = runtimeActionReceivedType(value);
|
|
7201
|
-
throw err;
|
|
7202
|
-
}
|
|
7203
|
-
}
|
|
7204
|
-
|
|
7205
|
-
function runtimeActionClientKeyProducerKind(type) {
|
|
7206
|
-
if (["agent_hire", "create_agent"].includes(type ?? "")) return "agent";
|
|
7207
|
-
if (type === "create_child_task") return "issue";
|
|
7208
|
-
return null;
|
|
7209
|
-
}
|
|
7210
|
-
|
|
7211
|
-
function runtimeActionClientKeyReferences(fields) {
|
|
7212
|
-
const type = readRuntimeActionType(fields);
|
|
7213
|
-
const references = [];
|
|
7214
|
-
if (["agent_hire", "create_agent"].includes(type ?? "")) {
|
|
7215
|
-
const clientKey = readString(fields.reportsToAgentClientKey);
|
|
7216
|
-
if (clientKey) references.push({ clientKey, expectedKind: "agent", field: "reportsToAgentClientKey" });
|
|
7217
|
-
}
|
|
7218
|
-
if (type === "create_child_task") {
|
|
7219
|
-
const assigneeClientKey = readString(fields.assigneeAgentClientKey) ?? readString(fields.agentClientKey);
|
|
7220
|
-
if (assigneeClientKey) references.push({ clientKey: assigneeClientKey, expectedKind: "agent", field: "assigneeAgentClientKey" });
|
|
7221
|
-
const parentClientKey = readString(fields.parentClientKey);
|
|
7222
|
-
if (parentClientKey) references.push({ clientKey: parentClientKey, expectedKind: "issue", field: "parentClientKey" });
|
|
7223
|
-
if (fields.blockedByClientKeys !== undefined) {
|
|
7224
|
-
assertRuntimeActionValid(Array.isArray(fields.blockedByClientKeys), "create_child_task blockedByClientKeys must be an array");
|
|
7225
|
-
for (const [index, value] of fields.blockedByClientKeys.entries()) {
|
|
7226
|
-
const clientKey = readString(value);
|
|
7227
|
-
assertRuntimeActionValid(clientKey, `create_child_task blockedByClientKeys[${index}] must be a non-empty string`);
|
|
7228
|
-
references.push({ clientKey, expectedKind: "issue", field: `blockedByClientKeys[${index}]` });
|
|
7229
|
-
}
|
|
7230
|
-
}
|
|
7231
|
-
}
|
|
7232
|
-
return references;
|
|
7233
|
-
}
|
|
7234
|
-
|
|
7235
|
-
function throwRuntimeActionGraphValidation(message, action, actionIndex, fieldPath) {
|
|
7236
|
-
const err = runtimeActionValidationError(message);
|
|
7237
|
-
err.runtimeActionType = readRuntimeActionType(action) ?? "unknown";
|
|
7238
|
-
err.runtimeActionOriginalType = readRuntimeActionOriginalType(action);
|
|
7239
|
-
err.runtimeActionFieldPath = fieldPath ?? `actions[${actionIndex}]`;
|
|
7240
|
-
throw err;
|
|
7241
|
-
}
|
|
7242
|
-
|
|
7243
|
-
function orderRuntimeActionsByClientKeyDependencies(actions) {
|
|
7244
|
-
const producers = new Map();
|
|
7245
|
-
for (const [index, action] of actions.entries()) {
|
|
7246
|
-
const fields = runtimeActionBusinessFields(action);
|
|
7247
|
-
const type = readRuntimeActionType(fields);
|
|
7248
|
-
const kind = runtimeActionClientKeyProducerKind(type);
|
|
7249
|
-
const clientKey = readString(fields.clientKey);
|
|
7250
|
-
if (!clientKey || !kind) continue;
|
|
7251
|
-
if (producers.has(clientKey)) {
|
|
7252
|
-
throwRuntimeActionGraphValidation(
|
|
7253
|
-
`runtime action clientKey must be unique: ${clientKey}`,
|
|
7254
|
-
action,
|
|
7255
|
-
index,
|
|
7256
|
-
`actions[${index}].clientKey`,
|
|
7257
|
-
);
|
|
7258
|
-
}
|
|
7259
|
-
producers.set(clientKey, { index, kind, type });
|
|
7260
|
-
}
|
|
7261
|
-
|
|
7262
|
-
const dependenciesByIndex = actions.map(() => new Set());
|
|
7263
|
-
const dependentsByIndex = actions.map(() => new Set());
|
|
7264
|
-
for (const [index, action] of actions.entries()) {
|
|
7265
|
-
const fields = runtimeActionBusinessFields(action);
|
|
7266
|
-
for (const reference of runtimeActionClientKeyReferences(fields)) {
|
|
7267
|
-
const producer = producers.get(reference.clientKey);
|
|
7268
|
-
if (!producer) {
|
|
7269
|
-
throwRuntimeActionGraphValidation(
|
|
7270
|
-
`${readRuntimeActionType(fields)} action references unknown ${reference.field}: ${reference.clientKey}`,
|
|
7271
|
-
action,
|
|
7272
|
-
index,
|
|
7273
|
-
`actions[${index}].${reference.field}`,
|
|
7274
|
-
);
|
|
7275
|
-
}
|
|
7276
|
-
if (producer.kind !== reference.expectedKind) {
|
|
7277
|
-
throwRuntimeActionGraphValidation(
|
|
7278
|
-
`${readRuntimeActionType(fields)} action ${reference.field} must reference a ${reference.expectedKind} clientKey: ${reference.clientKey}`,
|
|
7279
|
-
action,
|
|
7280
|
-
index,
|
|
7281
|
-
`actions[${index}].${reference.field}`,
|
|
7282
|
-
);
|
|
7283
|
-
}
|
|
7284
|
-
dependenciesByIndex[index].add(producer.index);
|
|
7285
|
-
dependentsByIndex[producer.index].add(index);
|
|
7286
|
-
}
|
|
7287
|
-
}
|
|
7288
|
-
|
|
7289
|
-
const ready = actions.map((_, index) => index).filter((index) => dependenciesByIndex[index].size === 0);
|
|
7290
|
-
const orderedIndexes = [];
|
|
7291
|
-
while (ready.length > 0) {
|
|
7292
|
-
ready.sort((left, right) => left - right);
|
|
7293
|
-
const index = ready.shift();
|
|
7294
|
-
orderedIndexes.push(index);
|
|
7295
|
-
for (const dependentIndex of dependentsByIndex[index]) {
|
|
7296
|
-
dependenciesByIndex[dependentIndex].delete(index);
|
|
7297
|
-
if (dependenciesByIndex[dependentIndex].size === 0) ready.push(dependentIndex);
|
|
7298
|
-
}
|
|
7299
|
-
}
|
|
7300
|
-
if (orderedIndexes.length !== actions.length) {
|
|
7301
|
-
const cycleIndex = dependenciesByIndex.findIndex((dependencies) => dependencies.size > 0);
|
|
7302
|
-
throwRuntimeActionGraphValidation(
|
|
7303
|
-
"runtime action clientKey dependency graph contains a cycle",
|
|
7304
|
-
actions[cycleIndex],
|
|
7305
|
-
cycleIndex,
|
|
7306
|
-
);
|
|
7307
|
-
}
|
|
7308
|
-
return orderedIndexes.map((index) => actions[index]);
|
|
7309
|
-
}
|
|
7310
|
-
|
|
7311
6823
|
function assertRuntimeInteractionOptions(options, path) {
|
|
7312
6824
|
assertRuntimeActionValid(Array.isArray(options) && options.length > 0 && options.length <= 10, `${path} requires 1-10 options`);
|
|
7313
6825
|
const ids = new Set();
|
|
@@ -7385,16 +6897,6 @@ function willRuntimeActionCreateRootBlockingChild(action) {
|
|
|
7385
6897
|
return fields.blockParentUntilDone === true || childStatus === "blocked";
|
|
7386
6898
|
}
|
|
7387
6899
|
|
|
7388
|
-
function willRuntimeActionCreateHumanOwnedInputPath(action) {
|
|
7389
|
-
const fields = runtimeActionBusinessFields(action);
|
|
7390
|
-
if (readRuntimeActionType(fields) !== "create_child_task") return false;
|
|
7391
|
-
const childStatus = readActionStatus(fields.status) ?? "todo";
|
|
7392
|
-
return Boolean(
|
|
7393
|
-
readString(fields.assigneeUserId) &&
|
|
7394
|
-
(fields.blockParentUntilDone === true || childStatus === "blocked"),
|
|
7395
|
-
);
|
|
7396
|
-
}
|
|
7397
|
-
|
|
7398
6900
|
function willRuntimeActionCreatePendingInteraction(action) {
|
|
7399
6901
|
const fields = runtimeActionBusinessFields(action);
|
|
7400
6902
|
if (readRuntimeActionType(fields) !== "create_interaction") return false;
|
|
@@ -7410,22 +6912,8 @@ function runtimeActionIsAmbiguousApprovalGatedChild(action) {
|
|
|
7410
6912
|
return childStatus !== "backlog";
|
|
7411
6913
|
}
|
|
7412
6914
|
|
|
7413
|
-
function
|
|
6915
|
+
function assertNoAmbiguousApprovalGatedChildren(actions) {
|
|
7414
6916
|
if (!actions.some(willRuntimeActionCreatePendingInteraction)) return;
|
|
7415
|
-
const ambiguousHire = actions.find((action) => {
|
|
7416
|
-
const fields = runtimeActionBusinessFields(action);
|
|
7417
|
-
return ["agent_hire", "create_agent"].includes(readRuntimeActionType(fields) ?? "") &&
|
|
7418
|
-
fields.executeBeforeConfirmation !== true;
|
|
7419
|
-
});
|
|
7420
|
-
if (ambiguousHire) {
|
|
7421
|
-
const error = runtimeActionValidationError(
|
|
7422
|
-
"approval-gated agent hire actions require the native hire approval only, a separate proposal interaction, or executeBeforeConfirmation true",
|
|
7423
|
-
);
|
|
7424
|
-
error.runtimeActionType = readRuntimeActionType(ambiguousHire) ?? "agent_hire";
|
|
7425
|
-
error.runtimeActionOriginalType = readRuntimeActionOriginalType(ambiguousHire);
|
|
7426
|
-
error.runtimeActionReason = error.message;
|
|
7427
|
-
throw error;
|
|
7428
|
-
}
|
|
7429
6917
|
const ambiguousChild = actions.find(runtimeActionIsAmbiguousApprovalGatedChild);
|
|
7430
6918
|
if (!ambiguousChild) return;
|
|
7431
6919
|
const error = new Error(
|
|
@@ -7437,29 +6925,6 @@ function assertNoAmbiguousApprovalGatedSideEffects(actions) {
|
|
|
7437
6925
|
throw error;
|
|
7438
6926
|
}
|
|
7439
6927
|
|
|
7440
|
-
function assertDelegatedParentWaitDisposition(actions) {
|
|
7441
|
-
const closesIndependentParentScope = actions.some((action) => {
|
|
7442
|
-
const fields = runtimeActionBusinessFields(action);
|
|
7443
|
-
if (readRuntimeActionType(fields) !== "update_parent") return false;
|
|
7444
|
-
return ["done", "cancelled"].includes(readActionStatus(fields.status) ?? "");
|
|
7445
|
-
});
|
|
7446
|
-
for (const [actionIndex, action] of actions.entries()) {
|
|
7447
|
-
const fields = runtimeActionBusinessFields(action);
|
|
7448
|
-
if (readRuntimeActionType(fields) !== "create_child_task") continue;
|
|
7449
|
-
if (readString(fields.parentClientKey)) continue;
|
|
7450
|
-
const status = readActionStatus(fields.status) ?? "todo";
|
|
7451
|
-
if (!["todo", "in_progress"].includes(status)) continue;
|
|
7452
|
-
if (fields.blockParentUntilDone === true || closesIndependentParentScope) continue;
|
|
7453
|
-
const err = runtimeActionValidationError(
|
|
7454
|
-
"delegated_parent_wait_required: root executable child tasks require blockParentUntilDone true, or the same batch must close the parent's independent scope with update_parent done/cancelled",
|
|
7455
|
-
);
|
|
7456
|
-
err.runtimeActionType = "create_child_task";
|
|
7457
|
-
err.runtimeActionFieldPath = `actions[${actionIndex}].blockParentUntilDone`;
|
|
7458
|
-
err.runtimeActionReason = err.message;
|
|
7459
|
-
throw err;
|
|
7460
|
-
}
|
|
7461
|
-
}
|
|
7462
|
-
|
|
7463
6928
|
function assertExplicitTaskDecomposition(command, actions) {
|
|
7464
6929
|
const requirement = commandLegacyTaskDecompositionRequirement(command);
|
|
7465
6930
|
if (!requirement.required) return;
|
|
@@ -7503,64 +6968,9 @@ function assertRuntimeActionsAllowedByContinuationScope(actions, scope) {
|
|
|
7503
6968
|
}
|
|
7504
6969
|
}
|
|
7505
6970
|
|
|
7506
|
-
function preflightRuntimeAction(action, preflightContext
|
|
6971
|
+
function preflightRuntimeAction(action, preflightContext) {
|
|
7507
6972
|
const type = readRuntimeActionType(action);
|
|
7508
6973
|
const fields = runtimeActionBusinessFields(action);
|
|
7509
|
-
assertNoUnsupportedRuntimeActionFields(action, actionIndex);
|
|
7510
|
-
assertRuntimeActionFieldTypes(action, actionIndex);
|
|
7511
|
-
if (type === "create_child_task") {
|
|
7512
|
-
assertNoPlaceholderRuntimeActionText(fields, ["title", "description"]);
|
|
7513
|
-
assertRuntimeActionValid(readBoundedActionString(fields, "title", 240), "create_child_task action requires title");
|
|
7514
|
-
if (fields.status !== undefined) {
|
|
7515
|
-
assertRuntimeActionValid(readActionStatus(fields.status), `create_child_task status is unsupported: ${String(fields.status)}`);
|
|
7516
|
-
}
|
|
7517
|
-
if (fields.priority !== undefined) {
|
|
7518
|
-
assertRuntimeActionValid(readActionPriority(fields.priority), `create_child_task priority is unsupported: ${String(fields.priority)}`);
|
|
7519
|
-
}
|
|
7520
|
-
assertRuntimeActionValid(
|
|
7521
|
-
!readString(fields.assigneeAgentId) || (!readString(fields.assigneeAgentClientKey) && !readString(fields.agentClientKey)),
|
|
7522
|
-
"create_child_task action cannot combine assigneeAgentId with assigneeAgentClientKey/agentClientKey",
|
|
7523
|
-
);
|
|
7524
|
-
if (fields.blockedByIssueIds !== undefined) {
|
|
7525
|
-
for (const [index, rawIssueId] of fields.blockedByIssueIds.entries()) {
|
|
7526
|
-
const issueId = readString(rawIssueId);
|
|
7527
|
-
assertRuntimeActionValid(
|
|
7528
|
-
issueId && /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(issueId),
|
|
7529
|
-
`create_child_task blockedByIssueIds[${index}] must be a UUID`,
|
|
7530
|
-
);
|
|
7531
|
-
}
|
|
7532
|
-
}
|
|
7533
|
-
if (fields.inputWorkProductIds !== undefined) {
|
|
7534
|
-
const seen = new Set();
|
|
7535
|
-
for (const [index, rawWorkProductId] of fields.inputWorkProductIds.entries()) {
|
|
7536
|
-
const workProductId = readString(rawWorkProductId);
|
|
7537
|
-
assertRuntimeActionValid(
|
|
7538
|
-
workProductId && /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(workProductId),
|
|
7539
|
-
`create_child_task inputWorkProductIds[${index}] must be a UUID`,
|
|
7540
|
-
);
|
|
7541
|
-
assertRuntimeActionValid(
|
|
7542
|
-
!seen.has(workProductId),
|
|
7543
|
-
`create_child_task inputWorkProductIds[${index}] duplicates ${workProductId}`,
|
|
7544
|
-
);
|
|
7545
|
-
seen.add(workProductId);
|
|
7546
|
-
}
|
|
7547
|
-
}
|
|
7548
|
-
return;
|
|
7549
|
-
}
|
|
7550
|
-
if (["agent_hire", "create_agent"].includes(type ?? "")) {
|
|
7551
|
-
assertNoPlaceholderRuntimeActionText(fields, ["name", "role", "title", "instructionsBundle"]);
|
|
7552
|
-
assertRuntimeActionValid(readBoundedActionString(fields, "name", 240), `${type} action requires name`);
|
|
7553
|
-
const reportingDispositionCount = [
|
|
7554
|
-
Boolean(readString(fields.reportsTo)),
|
|
7555
|
-
Boolean(readString(fields.reportsToAgentClientKey)),
|
|
7556
|
-
fields.topLevel === true,
|
|
7557
|
-
].filter(Boolean).length;
|
|
7558
|
-
assertRuntimeActionValid(
|
|
7559
|
-
reportingDispositionCount === 1,
|
|
7560
|
-
`${type} action requires exactly one of reportsTo, reportsToAgentClientKey, or topLevel true`,
|
|
7561
|
-
);
|
|
7562
|
-
return;
|
|
7563
|
-
}
|
|
7564
6974
|
if (type === "add_comment") {
|
|
7565
6975
|
assertNoPlaceholderRuntimeActionText(fields, ["body", "content", "text", "message"]);
|
|
7566
6976
|
const body = readBoundedActionString(fields, "body", 20_000)
|
|
@@ -7571,52 +6981,7 @@ function preflightRuntimeAction(action, preflightContext, actionIndex) {
|
|
|
7571
6981
|
return;
|
|
7572
6982
|
}
|
|
7573
6983
|
if (type === "upload_artifact") {
|
|
7574
|
-
assertNoPlaceholderRuntimeActionText(fields, ["path", "filePath", "artifactPath", "artifact_path", "title", "summary"]);
|
|
7575
|
-
assertRuntimeActionValid(readRuntimeArtifactPath(fields), "upload_artifact action requires path");
|
|
7576
|
-
const pathFields = ["path", "filePath", "artifactPath", "artifact_path"].filter((field) => readString(fields[field]));
|
|
7577
|
-
assertRuntimeActionValid(pathFields.length === 1, "upload_artifact action requires exactly one path field");
|
|
7578
|
-
const sourceWorkProductId = readString(fields.sourceWorkProductId);
|
|
7579
|
-
const sourceWorkProductIds = Array.isArray(fields.sourceWorkProductIds)
|
|
7580
|
-
? fields.sourceWorkProductIds.map(readString).filter(Boolean)
|
|
7581
|
-
: [];
|
|
7582
|
-
const supersedesWorkProductId = readString(fields.supersedesWorkProductId);
|
|
7583
|
-
assertRuntimeActionValid(
|
|
7584
|
-
Boolean(sourceWorkProductId) === Boolean(supersedesWorkProductId),
|
|
7585
|
-
"upload_artifact parent replacement requires both sourceWorkProductId and supersedesWorkProductId",
|
|
7586
|
-
);
|
|
7587
|
-
const evidenceRole = readString(fields.evidenceRole);
|
|
7588
|
-
assertRuntimeActionValid(
|
|
7589
|
-
!evidenceRole || ["source", "derived", "review"].includes(evidenceRole),
|
|
7590
|
-
`upload_artifact evidenceRole is unsupported: ${String(fields.evidenceRole)}`,
|
|
7591
|
-
);
|
|
7592
|
-
assertRuntimeActionValid(
|
|
7593
|
-
!["derived", "review"].includes(evidenceRole ?? "") || sourceWorkProductIds.length > 0 || Boolean(sourceWorkProductId),
|
|
7594
|
-
`upload_artifact evidenceRole=${evidenceRole} requires sourceWorkProductIds`,
|
|
7595
|
-
);
|
|
7596
|
-
if (fields.sourceWorkProductIds !== undefined) {
|
|
7597
|
-
assertRuntimeActionValid(Array.isArray(fields.sourceWorkProductIds), "upload_artifact sourceWorkProductIds must be an array");
|
|
7598
|
-
assertRuntimeActionValid(sourceWorkProductIds.length === fields.sourceWorkProductIds.length, "upload_artifact sourceWorkProductIds must contain non-empty strings");
|
|
7599
|
-
}
|
|
7600
|
-
if (fields.scenarioKeys !== undefined) {
|
|
7601
|
-
assertRuntimeActionValid(Array.isArray(fields.scenarioKeys), "upload_artifact scenarioKeys must be an array");
|
|
7602
|
-
assertRuntimeActionValid(fields.scenarioKeys.map(readString).filter(Boolean).length === fields.scenarioKeys.length, "upload_artifact scenarioKeys must contain non-empty strings");
|
|
7603
|
-
}
|
|
7604
6984
|
assertRuntimeArtifactContentType(fields, preflightContext.allowedContentTypes);
|
|
7605
|
-
resolveWorkspaceArtifactPath(fields, { cwd: preflightContext.cwd });
|
|
7606
|
-
return;
|
|
7607
|
-
}
|
|
7608
|
-
if (type === "upsert_document") {
|
|
7609
|
-
assertNoPlaceholderRuntimeActionText(fields, ["key", "title", "body", "changeSummary"]);
|
|
7610
|
-
const key = readBoundedActionString(fields, "key", 64);
|
|
7611
|
-
const body = readBoundedActionString(fields, "body", 524_288);
|
|
7612
|
-
assertRuntimeActionValid(key && /^[a-z0-9][a-z0-9_-]*$/.test(key), "upsert_document key must use lowercase letters, numbers, _ or -");
|
|
7613
|
-
assertRuntimeActionValid(body, "upsert_document action requires body");
|
|
7614
|
-
assertRuntimeActionValid(fields.format === "markdown", "upsert_document format must be markdown");
|
|
7615
|
-
const baseRevisionId = readString(fields.baseRevisionId);
|
|
7616
|
-
assertRuntimeActionValid(
|
|
7617
|
-
!baseRevisionId || /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(baseRevisionId),
|
|
7618
|
-
"upsert_document baseRevisionId must be a UUID",
|
|
7619
|
-
);
|
|
7620
6985
|
return;
|
|
7621
6986
|
}
|
|
7622
6987
|
if (type === "create_interaction") {
|
|
@@ -7627,27 +6992,12 @@ function preflightRuntimeAction(action, preflightContext, actionIndex) {
|
|
|
7627
6992
|
["request_confirmation", "request_checkbox_confirmation", "ask_user_questions", "suggest_tasks"].includes(kind),
|
|
7628
6993
|
`create_interaction action does not support kind: ${kind}`,
|
|
7629
6994
|
);
|
|
7630
|
-
assertRuntimeActionValid(
|
|
7631
|
-
kind !== "ask_user_questions" || readString(fields.continuationPolicy) !== "wake_assignee_on_accept",
|
|
7632
|
-
"ask_user_questions continuationPolicy cannot be wake_assignee_on_accept; use wake_assignee so answering the questions resumes the assignee",
|
|
7633
|
-
);
|
|
7634
6995
|
const payload = normalizeCreateInteractionPayload(fields, kind);
|
|
7635
6996
|
assertRuntimeActionValid(
|
|
7636
6997
|
Object.keys(payload).length > 0,
|
|
7637
6998
|
`create_interaction.${kind} action requires payload; expected shape is available in runtimeActionDiagnostics.expectedShape`,
|
|
7638
6999
|
);
|
|
7639
7000
|
assertRuntimeCreateInteractionPayload(kind, payload);
|
|
7640
|
-
const confirmationTarget = asRecord(payload.target);
|
|
7641
|
-
if (
|
|
7642
|
-
preflightContext.acceptedConfirmationContinuation &&
|
|
7643
|
-
["request_confirmation", "request_checkbox_confirmation"].includes(kind) &&
|
|
7644
|
-
(!readString(confirmationTarget.type) || !readString(confirmationTarget.key))
|
|
7645
|
-
) {
|
|
7646
|
-
assertRuntimeActionValid(
|
|
7647
|
-
false,
|
|
7648
|
-
"accepted_confirmation_decision_target_required: an accepted confirmation continuation requires payload.target.type and a non-empty payload.target.key so the server can identify the decision; use a new target revision for genuinely changed content or finish the accepted work",
|
|
7649
|
-
);
|
|
7650
|
-
}
|
|
7651
7001
|
preflightContext.createdReviewPathForInReview = true;
|
|
7652
7002
|
return;
|
|
7653
7003
|
}
|
|
@@ -7656,20 +7006,6 @@ function preflightRuntimeAction(action, preflightContext, actionIndex) {
|
|
|
7656
7006
|
const status = readActionStatus(fields.status);
|
|
7657
7007
|
const comment = readBoundedActionString(fields, "comment", 20_000);
|
|
7658
7008
|
assertRuntimeActionValid(status || comment, "update_parent action requires status or comment");
|
|
7659
|
-
if (fields.status !== undefined) {
|
|
7660
|
-
assertRuntimeActionValid(status, `update_parent status is unsupported: ${String(fields.status)}`);
|
|
7661
|
-
}
|
|
7662
|
-
if (
|
|
7663
|
-
status === "done" &&
|
|
7664
|
-
runtimeActionDoneNeedsExternalHuman(fields, preflightContext) &&
|
|
7665
|
-
!preflightContext.createdHumanOwnedInputPath &&
|
|
7666
|
-
fields.force !== true
|
|
7667
|
-
) {
|
|
7668
|
-
assertRuntimeActionValid(
|
|
7669
|
-
false,
|
|
7670
|
-
"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",
|
|
7671
|
-
);
|
|
7672
|
-
}
|
|
7673
7009
|
if (
|
|
7674
7010
|
status === "in_review" &&
|
|
7675
7011
|
!preflightContext.createdReviewPathForInReview &&
|
|
@@ -7682,45 +7018,22 @@ function preflightRuntimeAction(action, preflightContext, actionIndex) {
|
|
|
7682
7018
|
) {
|
|
7683
7019
|
preflightContext.createdReviewPathForInReview = true;
|
|
7684
7020
|
}
|
|
7685
|
-
return;
|
|
7686
|
-
}
|
|
7687
|
-
if (type === "update_company_profile") {
|
|
7688
|
-
const profileFields = ["tagline", "stage", "targetCustomers", "products", "currentPriority", "businessModel", "regions", "brandTone"];
|
|
7689
|
-
assertRuntimeActionValid(
|
|
7690
|
-
profileFields.some((field) => fields[field] === null || readBoundedActionString(fields, field, 2000)),
|
|
7691
|
-
"update_company_profile action requires at least one profile field",
|
|
7692
|
-
);
|
|
7693
|
-
return;
|
|
7694
|
-
}
|
|
7695
|
-
if (type === "create_routine") {
|
|
7696
|
-
assertRuntimeActionValid(readBoundedActionString(fields, "title", 240), "create_routine action requires title");
|
|
7697
|
-
assertRuntimeActionValid(readBoundedActionString(fields, "cronExpression", 120), "create_routine action requires cronExpression");
|
|
7698
|
-
if (fields.priority !== undefined) {
|
|
7699
|
-
assertRuntimeActionValid(readActionPriority(fields.priority), `create_routine priority is unsupported: ${String(fields.priority)}`);
|
|
7700
|
-
}
|
|
7701
7021
|
}
|
|
7702
7022
|
}
|
|
7703
7023
|
|
|
7704
7024
|
function preflightRuntimeActionBatch(actions, options = {}) {
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
const orderedActions = orderRuntimeActionsByClientKeyDependencies(actions);
|
|
7708
|
-
assertRuntimeActionsAllowedByContinuationScope(orderedActions, options.continuationScope);
|
|
7709
|
-
assertNoAmbiguousApprovalGatedSideEffects(orderedActions);
|
|
7025
|
+
assertRuntimeActionsAllowedByContinuationScope(actions, options.continuationScope);
|
|
7026
|
+
assertNoAmbiguousApprovalGatedChildren(actions);
|
|
7710
7027
|
const preflightContext = {
|
|
7711
7028
|
createdReviewPathForInReview: false,
|
|
7712
7029
|
createdRootBlockingChildForInReview: false,
|
|
7713
|
-
createdHumanOwnedInputPath: false,
|
|
7714
7030
|
answeredQuestionContinuation: options.answeredQuestionContinuation === true,
|
|
7715
|
-
acceptedConfirmationContinuation: options.acceptedConfirmationContinuation === true,
|
|
7716
7031
|
allowedContentTypes: Array.isArray(options.allowedContentTypes) ? options.allowedContentTypes : [],
|
|
7717
|
-
cwd: readString(options.cwd),
|
|
7718
7032
|
finalCommentBodies: [],
|
|
7719
7033
|
};
|
|
7720
|
-
for (const action of
|
|
7721
|
-
const actionIndex = actionIndexByObject.get(action);
|
|
7034
|
+
for (const action of actions) {
|
|
7722
7035
|
try {
|
|
7723
|
-
preflightRuntimeAction(action, preflightContext
|
|
7036
|
+
preflightRuntimeAction(action, preflightContext);
|
|
7724
7037
|
const fields = runtimeActionBusinessFields(action);
|
|
7725
7038
|
if (readRuntimeActionType(fields) === "add_comment") {
|
|
7726
7039
|
const body = readBoundedActionString(fields, "body", 20_000)
|
|
@@ -7732,9 +7045,6 @@ function preflightRuntimeActionBatch(actions, options = {}) {
|
|
|
7732
7045
|
if (willRuntimeActionCreateRootBlockingChild(action)) {
|
|
7733
7046
|
preflightContext.createdRootBlockingChildForInReview = true;
|
|
7734
7047
|
}
|
|
7735
|
-
if (willRuntimeActionCreateHumanOwnedInputPath(action)) {
|
|
7736
|
-
preflightContext.createdHumanOwnedInputPath = true;
|
|
7737
|
-
}
|
|
7738
7048
|
if (willRuntimeActionCreateReviewPath(action)) {
|
|
7739
7049
|
preflightContext.createdReviewPathForInReview = true;
|
|
7740
7050
|
}
|
|
@@ -7751,40 +7061,6 @@ function preflightRuntimeActionBatch(actions, options = {}) {
|
|
|
7751
7061
|
throw err;
|
|
7752
7062
|
}
|
|
7753
7063
|
}
|
|
7754
|
-
return orderedActions;
|
|
7755
|
-
}
|
|
7756
|
-
|
|
7757
|
-
function runtimeActionAuthorizationClass(action) {
|
|
7758
|
-
// Keep these high-risk route mappings aligned with server-side checks in
|
|
7759
|
-
// routes/agents.ts (organization_mutation) and routes/issues.ts
|
|
7760
|
-
// (executable_child). Unknown action types intentionally remain governance.
|
|
7761
|
-
const fields = runtimeActionBusinessFields(action);
|
|
7762
|
-
const type = readRuntimeActionType(fields);
|
|
7763
|
-
if (["agent_hire", "create_agent"].includes(type ?? "")) return "organization_mutation";
|
|
7764
|
-
if (
|
|
7765
|
-
type === "create_child_task" &&
|
|
7766
|
-
["todo", "in_progress"].includes(readActionStatus(fields.status) ?? "todo")
|
|
7767
|
-
) return "executable_child";
|
|
7768
|
-
return "task_governance";
|
|
7769
|
-
}
|
|
7770
|
-
|
|
7771
|
-
function assertRuntimeActionsAllowedByAuthorizationEnvelope(actions, envelope) {
|
|
7772
|
-
for (const [actionIndex, action] of actions.entries()) {
|
|
7773
|
-
const requiredAuthorization = runtimeActionAuthorizationClass(action);
|
|
7774
|
-
if (envelope.allowedActionClasses.has(requiredAuthorization)) continue;
|
|
7775
|
-
const type = readRuntimeActionType(runtimeActionBusinessFields(action)) ?? "unknown";
|
|
7776
|
-
const err = runtimeActionValidationError(
|
|
7777
|
-
`${type} action at index ${actionIndex} requires authorization class ${requiredAuthorization}; ` +
|
|
7778
|
-
`source ${envelope.sourceKind ?? "unknown"}:${envelope.sourceId ?? "unknown"} revision ${envelope.sourceRevision ?? "unknown"}`,
|
|
7779
|
-
);
|
|
7780
|
-
err.runtimeActionType = type;
|
|
7781
|
-
err.runtimeActionFieldPath = `actions[${actionIndex}]`;
|
|
7782
|
-
err.runtimeActionRequiredAuthorization = requiredAuthorization;
|
|
7783
|
-
err.runtimeActionAuthorizationSourceKind = envelope.sourceKind;
|
|
7784
|
-
err.runtimeActionAuthorizationSourceId = envelope.sourceId;
|
|
7785
|
-
err.runtimeActionAuthorizationSourceRevision = envelope.sourceRevision;
|
|
7786
|
-
throw err;
|
|
7787
|
-
}
|
|
7788
7064
|
}
|
|
7789
7065
|
|
|
7790
7066
|
function normalizeRuntimeActionPlaceholderText(value) {
|
|
@@ -7815,7 +7091,6 @@ const SINGLE_KEY_RUNTIME_ACTION_TYPES = new Set([
|
|
|
7815
7091
|
"create_child_task",
|
|
7816
7092
|
"create_interaction",
|
|
7817
7093
|
"create_routine",
|
|
7818
|
-
"upsert_document",
|
|
7819
7094
|
"update_company_profile",
|
|
7820
7095
|
"update_parent",
|
|
7821
7096
|
"upload_artifact",
|
|
@@ -7888,10 +7163,6 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
7888
7163
|
priority: readActionPriority(fields.priority) ?? "medium",
|
|
7889
7164
|
assigneeAgentId: runtimeChildAssigneeAgentId(fields, runtimeAuth, command, actionContext),
|
|
7890
7165
|
assigneeUserId: readString(fields.assigneeUserId),
|
|
7891
|
-
blockedByIssueIds: runtimeActionBlockedByIssueIds(fields, actionContext),
|
|
7892
|
-
...(Array.isArray(fields.inputWorkProductIds)
|
|
7893
|
-
? { inputWorkProductIds: fields.inputWorkProductIds.map(readString).filter(Boolean) }
|
|
7894
|
-
: {}),
|
|
7895
7166
|
blockParentUntilDone,
|
|
7896
7167
|
originKind: "amaster_runtime_child_task",
|
|
7897
7168
|
originId: issueId,
|
|
@@ -7902,23 +7173,6 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
7902
7173
|
idempotencyKey: originFingerprint,
|
|
7903
7174
|
});
|
|
7904
7175
|
const createdIssueId = readString(asRecord(created).id);
|
|
7905
|
-
const persistedAssigneeAgentId = readString(asRecord(created).assigneeAgentId);
|
|
7906
|
-
if (body.assigneeAgentId && persistedAssigneeAgentId !== body.assigneeAgentId) {
|
|
7907
|
-
throw new Error(
|
|
7908
|
-
`runtime_action_postcondition_failed: child assignee mismatch; expected ${body.assigneeAgentId}, received ${persistedAssigneeAgentId ?? "missing"}`,
|
|
7909
|
-
);
|
|
7910
|
-
}
|
|
7911
|
-
if (body.blockedByIssueIds.length > 0) {
|
|
7912
|
-
const persistedBlockerIds = Array.isArray(asRecord(created).blockedBy)
|
|
7913
|
-
? asRecord(created).blockedBy.map((entry) => readString(asRecord(entry).id)).filter(Boolean)
|
|
7914
|
-
: [];
|
|
7915
|
-
const missingBlockerIds = body.blockedByIssueIds.filter((blockerId) => !persistedBlockerIds.includes(blockerId));
|
|
7916
|
-
if (missingBlockerIds.length > 0) {
|
|
7917
|
-
throw new Error(
|
|
7918
|
-
`runtime_action_postcondition_failed: child blockers were not persisted: ${missingBlockerIds.join(", ")}`,
|
|
7919
|
-
);
|
|
7920
|
-
}
|
|
7921
|
-
}
|
|
7922
7176
|
const clientKey = runtimeActionResultKey(fields);
|
|
7923
7177
|
if (createdIssueId) actionContext.createdChildIssueIdByFingerprint.set(duplicateKey, createdIssueId);
|
|
7924
7178
|
if (createdIssueId && clientKey) actionContext.issueIdByClientKey.set(clientKey, createdIssueId);
|
|
@@ -7958,8 +7212,6 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
7958
7212
|
adapterType: runtimeActionCurrentAgentAdapterType(command),
|
|
7959
7213
|
runtimeConfig: runtimeActionCurrentAgentRuntimeConnector(command),
|
|
7960
7214
|
});
|
|
7961
|
-
const reportsTo = runtimeActionReportsToAgentId(fields, actionContext);
|
|
7962
|
-
if (reportsTo) body.reportsTo = reportsTo;
|
|
7963
7215
|
const idempotencyKey = runtimeAgentHireActionFingerprint(fields, companyId, issueId, body);
|
|
7964
7216
|
body.idempotencyKey = idempotencyKey;
|
|
7965
7217
|
const created = asRecord(await runtimeApiJson(
|
|
@@ -7973,16 +7225,6 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
7973
7225
|
const approval = asRecord(created.approval);
|
|
7974
7226
|
const clientKey = runtimeActionResultKey(fields);
|
|
7975
7227
|
const agentId = readString(agent.id);
|
|
7976
|
-
if (reportsTo && readString(agent.reportsTo) !== reportsTo) {
|
|
7977
|
-
throw new Error(
|
|
7978
|
-
`runtime_action_postcondition_failed: agent reportsTo mismatch; expected ${reportsTo}, received ${readString(agent.reportsTo) ?? "missing"}`,
|
|
7979
|
-
);
|
|
7980
|
-
}
|
|
7981
|
-
if (fields.topLevel === true && readString(agent.reportsTo)) {
|
|
7982
|
-
throw new Error(
|
|
7983
|
-
`runtime_action_postcondition_failed: top-level agent reportsTo mismatch; expected missing, received ${readString(agent.reportsTo)}`,
|
|
7984
|
-
);
|
|
7985
|
-
}
|
|
7986
7228
|
if (agentId && clientKey) actionContext.agentIdByClientKey.set(clientKey, agentId);
|
|
7987
7229
|
return {
|
|
7988
7230
|
type,
|
|
@@ -7995,8 +7237,6 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
7995
7237
|
approvalStatus: readString(approval.status),
|
|
7996
7238
|
sourceIssueId: readString(body.sourceIssueId) ?? null,
|
|
7997
7239
|
sourceIssueIds: runtimeActionIssueIdList(body.sourceIssueIds),
|
|
7998
|
-
reportsTo: reportsTo ?? null,
|
|
7999
|
-
topLevel: fields.topLevel === true,
|
|
8000
7240
|
...(clientKey ? { clientKey } : {}),
|
|
8001
7241
|
reusedExisting: asRecord(created).reusedExisting === true,
|
|
8002
7242
|
};
|
|
@@ -8030,6 +7270,25 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
8030
7270
|
{ key: "idempotencyKey", placeholders: ["artifact-key", "artifact key", "upload-artifact-key", "upload artifact key"] },
|
|
8031
7271
|
]);
|
|
8032
7272
|
const rawPath = readRuntimeArtifactPath(fields);
|
|
7273
|
+
if (!rawPath) {
|
|
7274
|
+
const title = readBoundedActionString(fields, "title", 240) ?? "运行产物";
|
|
7275
|
+
const summary = readBoundedActionString(fields, "summary", 20_000);
|
|
7276
|
+
const body = [
|
|
7277
|
+
`AMaster 未上传产物 \`${title}\`,因为 runtime action 没有提供工作区内文件路径。`,
|
|
7278
|
+
summary ? `\n${summary}` : "",
|
|
7279
|
+
"\n请在后续运行中先把文件写入执行工作区,再用 upload_artifact.path 引用该相对路径。",
|
|
7280
|
+
].join("");
|
|
7281
|
+
const created = await runtimeApiJson(runtimeAuth, "POST", `/api/issues/${encodeURIComponent(issueId)}/comments`, { body });
|
|
7282
|
+
actionContext.finalCommentBodies.push(body);
|
|
7283
|
+
return {
|
|
7284
|
+
type,
|
|
7285
|
+
...(originalType && originalType !== type ? { originalType } : {}),
|
|
7286
|
+
status: "skipped",
|
|
7287
|
+
reason: "missing_path",
|
|
7288
|
+
title,
|
|
7289
|
+
commentId: readString(asRecord(created).id),
|
|
7290
|
+
};
|
|
7291
|
+
}
|
|
8033
7292
|
const payload = asRecord(command.payload);
|
|
8034
7293
|
const companyId = readString(payload.companyId) ?? readString(runtimeAuth.companyId);
|
|
8035
7294
|
if (!companyId) throw new Error("upload_artifact action requires companyId");
|
|
@@ -8051,25 +7310,15 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
8051
7310
|
const title = readBoundedActionString(fields, "title", 240) ?? filename;
|
|
8052
7311
|
if (fields.createWorkProduct !== false) {
|
|
8053
7312
|
const existingWorkProducts = await listExistingIssueWorkProducts(runtimeAuth, issueId);
|
|
8054
|
-
const existingWorkProduct = existingExplicitArtifactUploadMatch(filePath, title, existingWorkProducts
|
|
7313
|
+
const existingWorkProduct = existingExplicitArtifactUploadMatch(filePath, title, existingWorkProducts);
|
|
8055
7314
|
if (existingWorkProduct) {
|
|
8056
7315
|
const metadata = asRecord(existingWorkProduct.metadata);
|
|
8057
|
-
const existingWorkProductId = readString(existingWorkProduct.id);
|
|
8058
|
-
const existingAttachmentId = readString(metadata.attachmentId);
|
|
8059
|
-
if (!existingWorkProductId || !existingAttachmentId) {
|
|
8060
|
-
throw new Error("runtime_action_postcondition_failed: reused artifact is missing work product or attachment identity");
|
|
8061
|
-
}
|
|
8062
|
-
await verifyUploadedArtifactPostcondition(runtimeAuth, issueId, {
|
|
8063
|
-
attachmentId: existingAttachmentId,
|
|
8064
|
-
workProductId: existingWorkProductId,
|
|
8065
|
-
sha256,
|
|
8066
|
-
}, fields);
|
|
8067
7316
|
return {
|
|
8068
7317
|
type,
|
|
8069
7318
|
...(originalType && originalType !== type ? { originalType } : {}),
|
|
8070
7319
|
path: filePath,
|
|
8071
|
-
attachmentId:
|
|
8072
|
-
workProductId:
|
|
7320
|
+
attachmentId: readString(metadata.attachmentId),
|
|
7321
|
+
workProductId: readString(existingWorkProduct.id),
|
|
8073
7322
|
sha256: readString(metadata.sha256) ?? sha256,
|
|
8074
7323
|
byteSize: readNumber(metadata.byteSize, fileBytes.byteLength),
|
|
8075
7324
|
originalFilename: readString(metadata.originalFilename) ?? filename,
|
|
@@ -8091,12 +7340,11 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
8091
7340
|
if (!attachmentId) throw new Error("upload_artifact attachment response did not include id");
|
|
8092
7341
|
|
|
8093
7342
|
let workProduct = null;
|
|
8094
|
-
|
|
8095
|
-
|
|
8096
|
-
|
|
8097
|
-
|
|
8098
|
-
|
|
8099
|
-
workProduct = asRecord(await runtimeApiJson(runtimeAuth, "POST", `/api/issues/${encodeURIComponent(issueId)}/work-products`, {
|
|
7343
|
+
if (fields.createWorkProduct !== false) {
|
|
7344
|
+
const contentPath = readString(attachment.contentPath) ?? `/api/attachments/${attachmentId}/content`;
|
|
7345
|
+
const openPath = readString(attachment.openPath) ?? contentPath;
|
|
7346
|
+
const downloadPath = readString(attachment.downloadPath) ?? `${contentPath}?download=1`;
|
|
7347
|
+
workProduct = asRecord(await runtimeApiJson(runtimeAuth, "POST", `/api/issues/${encodeURIComponent(issueId)}/work-products`, {
|
|
8100
7348
|
type: "artifact",
|
|
8101
7349
|
provider: readString(fields.provider) ?? "paperclip",
|
|
8102
7350
|
title,
|
|
@@ -8115,26 +7363,8 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
8115
7363
|
openPath,
|
|
8116
7364
|
downloadPath,
|
|
8117
7365
|
originalFilename: readString(attachment.originalFilename) ?? filename,
|
|
8118
|
-
...(readString(fields.sourceWorkProductId) ? { sourceWorkProductId: readString(fields.sourceWorkProductId) } : {}),
|
|
8119
|
-
...(Array.isArray(fields.sourceWorkProductIds) ? { sourceWorkProductIds: fields.sourceWorkProductIds.map(readString).filter(Boolean) } : {}),
|
|
8120
|
-
...(readString(fields.supersedesWorkProductId) ? { supersedesWorkProductId: readString(fields.supersedesWorkProductId) } : {}),
|
|
8121
|
-
...(readString(fields.evidenceRole) ? { evidenceRole: readString(fields.evidenceRole) } : {}),
|
|
8122
|
-
...(Array.isArray(fields.scenarioKeys) ? { scenarioKeys: fields.scenarioKeys.map(readString).filter(Boolean) } : {}),
|
|
8123
7366
|
},
|
|
8124
|
-
|
|
8125
|
-
const workProductId = readString(workProduct.id);
|
|
8126
|
-
if (!workProductId) {
|
|
8127
|
-
throw new Error("runtime_action_postcondition_failed: artifact work product response did not include id");
|
|
8128
|
-
}
|
|
8129
|
-
await verifyUploadedArtifactPostcondition(runtimeAuth, issueId, {
|
|
8130
|
-
attachmentId,
|
|
8131
|
-
workProductId,
|
|
8132
|
-
sha256: readString(attachment.sha256) ?? sha256,
|
|
8133
|
-
}, fields);
|
|
8134
|
-
}
|
|
8135
|
-
} catch (err) {
|
|
8136
|
-
await cleanupFailedArtifactUpload(config, command, runtimeAuth, attachmentId, readString(workProduct?.id), err);
|
|
8137
|
-
throw err;
|
|
7367
|
+
}));
|
|
8138
7368
|
}
|
|
8139
7369
|
|
|
8140
7370
|
return {
|
|
@@ -8192,37 +7422,6 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
8192
7422
|
};
|
|
8193
7423
|
}
|
|
8194
7424
|
|
|
8195
|
-
if (type === "upsert_document") {
|
|
8196
|
-
assertNoPlaceholderRuntimeActionText(fields, ["key", "title", "body", "changeSummary"]);
|
|
8197
|
-
const key = readBoundedActionString(fields, "key", 64);
|
|
8198
|
-
if (!key || !/^[a-z0-9][a-z0-9_-]*$/.test(key)) {
|
|
8199
|
-
throw new Error("upsert_document key must use lowercase letters, numbers, _ or -");
|
|
8200
|
-
}
|
|
8201
|
-
const body = readBoundedActionString(fields, "body", 524_288);
|
|
8202
|
-
if (!body) throw new Error("upsert_document action requires body");
|
|
8203
|
-
if (fields.format !== "markdown") throw new Error("upsert_document format must be markdown");
|
|
8204
|
-
const document = asRecord(await runtimeApiJson(
|
|
8205
|
-
runtimeAuth,
|
|
8206
|
-
"PUT",
|
|
8207
|
-
`/api/issues/${encodeURIComponent(issueId)}/documents/${encodeURIComponent(key)}`,
|
|
8208
|
-
{
|
|
8209
|
-
title: readBoundedActionString(fields, "title", 200) ?? null,
|
|
8210
|
-
format: "markdown",
|
|
8211
|
-
body,
|
|
8212
|
-
changeSummary: readBoundedActionString(fields, "changeSummary", 500) ?? null,
|
|
8213
|
-
baseRevisionId: readString(fields.baseRevisionId) ?? null,
|
|
8214
|
-
},
|
|
8215
|
-
));
|
|
8216
|
-
return {
|
|
8217
|
-
type,
|
|
8218
|
-
...(originalType && originalType !== type ? { originalType } : {}),
|
|
8219
|
-
issueId,
|
|
8220
|
-
key,
|
|
8221
|
-
documentId: readString(document.id),
|
|
8222
|
-
revisionNumber: typeof document.latestRevisionNumber === "number" ? document.latestRevisionNumber : undefined,
|
|
8223
|
-
};
|
|
8224
|
-
}
|
|
8225
|
-
|
|
8226
7425
|
if (type === "update_parent") {
|
|
8227
7426
|
assertNoPlaceholderRuntimeActionText(fields, ["comment"]);
|
|
8228
7427
|
const patch = {};
|
|
@@ -8238,6 +7437,18 @@ async function applyRuntimeAction(config, command, runtimeAuth, issueId, action,
|
|
|
8238
7437
|
if (Object.keys(patch).length === 0) {
|
|
8239
7438
|
throw new Error("update_parent action requires status or comment");
|
|
8240
7439
|
}
|
|
7440
|
+
if (status === "done" && runtimeActionDoneNeedsExternalHuman(fields, actionContext) && fields.force !== true) {
|
|
7441
|
+
Object.assign(patch, runtimeActionExternalHumanPatch(comment));
|
|
7442
|
+
const updated = await runtimeApiJson(runtimeAuth, "PATCH", `/api/issues/${encodeURIComponent(issueId)}`, patch);
|
|
7443
|
+
return {
|
|
7444
|
+
type,
|
|
7445
|
+
...(originalType && originalType !== type ? { originalType } : {}),
|
|
7446
|
+
issueId: readString(asRecord(updated).id),
|
|
7447
|
+
status: readString(asRecord(updated).status) ?? "blocked",
|
|
7448
|
+
coercedFromStatus: "done",
|
|
7449
|
+
externalOwnerAction: true,
|
|
7450
|
+
};
|
|
7451
|
+
}
|
|
8241
7452
|
if (status === "done" && actionContext.createdReviewPathForInReview && fields.force !== true) {
|
|
8242
7453
|
patch.status = "in_review";
|
|
8243
7454
|
patch.comment = comment
|
|
@@ -8493,48 +7704,6 @@ function runtimeActionFailureSummary(action, err) {
|
|
|
8493
7704
|
};
|
|
8494
7705
|
}
|
|
8495
7706
|
|
|
8496
|
-
function runtimeActionLedgerResourceIds(result) {
|
|
8497
|
-
const record = asRecord(result);
|
|
8498
|
-
const resourceIds = {};
|
|
8499
|
-
for (const key of ["issueId", "parentIssueId", "agentId", "approvalId", "commentId", "interactionId", "attachmentId", "workProductId", "routineId", "profileId"]) {
|
|
8500
|
-
const value = readString(record[key]);
|
|
8501
|
-
if (value) resourceIds[key] = value;
|
|
8502
|
-
}
|
|
8503
|
-
return resourceIds;
|
|
8504
|
-
}
|
|
8505
|
-
|
|
8506
|
-
function runtimeActionLedgerPostconditionStatus(action, status, error) {
|
|
8507
|
-
if (status === "failed") {
|
|
8508
|
-
return /runtime_action_postcondition_failed/i.test(String(error instanceof Error ? error.message : error ?? ""))
|
|
8509
|
-
? "failed"
|
|
8510
|
-
: "not_run";
|
|
8511
|
-
}
|
|
8512
|
-
if (status !== "applied" && status !== "reused") return "not_run";
|
|
8513
|
-
return ["create_child_task", "agent_hire", "create_agent", "upload_artifact"].includes(
|
|
8514
|
-
readRuntimeActionType(action) ?? "",
|
|
8515
|
-
)
|
|
8516
|
-
? "passed"
|
|
8517
|
-
: "not_applicable";
|
|
8518
|
-
}
|
|
8519
|
-
|
|
8520
|
-
function runtimeActionLedgerEntry(action, actionIndex, status, result = null, error = null) {
|
|
8521
|
-
const fields = runtimeActionBusinessFields(action);
|
|
8522
|
-
const resultRecord = asRecord(result);
|
|
8523
|
-
const clientKey = readString(fields.clientKey);
|
|
8524
|
-
const resourceIds = runtimeActionLedgerResourceIds(resultRecord);
|
|
8525
|
-
return {
|
|
8526
|
-
actionIndex,
|
|
8527
|
-
actionType: readRuntimeActionType(fields) ?? "unknown",
|
|
8528
|
-
actionKey: clientKey ?? `index:${actionIndex}`,
|
|
8529
|
-
...(clientKey ? { clientKey } : {}),
|
|
8530
|
-
status,
|
|
8531
|
-
reusedExisting: resultRecord.reusedExisting === true,
|
|
8532
|
-
postconditionStatus: runtimeActionLedgerPostconditionStatus(fields, status, error),
|
|
8533
|
-
...(Object.keys(resourceIds).length > 0 ? { resourceIds } : {}),
|
|
8534
|
-
...(error ? { error: truncateText(error instanceof Error ? error.message : String(error), 1000) } : {}),
|
|
8535
|
-
};
|
|
8536
|
-
}
|
|
8537
|
-
|
|
8538
7707
|
function isSkippedRuntimeActionResult(result) {
|
|
8539
7708
|
return readString(asRecord(result).status) === "skipped";
|
|
8540
7709
|
}
|
|
@@ -8678,16 +7847,6 @@ function runtimeActionExpectedShape(actionType, actionKind) {
|
|
|
8678
7847
|
if (actionType === "upload_artifact") {
|
|
8679
7848
|
return { type: "upload_artifact", path: "relative/path.ext", title: "Artifact title", summary: "Artifact summary" };
|
|
8680
7849
|
}
|
|
8681
|
-
if (actionType === "upsert_document") {
|
|
8682
|
-
return {
|
|
8683
|
-
type: "upsert_document",
|
|
8684
|
-
key: "business-brief",
|
|
8685
|
-
title: "2026-07-16 每日经营简报",
|
|
8686
|
-
body: "# 每日经营简报\n\n经营摘要。",
|
|
8687
|
-
format: "markdown",
|
|
8688
|
-
changeSummary: "生成每日经营简报",
|
|
8689
|
-
};
|
|
8690
|
-
}
|
|
8691
7850
|
if (actionType === "agent_hire" || actionType === "create_agent") {
|
|
8692
7851
|
return {
|
|
8693
7852
|
type: actionType,
|
|
@@ -8725,12 +7884,6 @@ function runtimeActionDiagnostics(err, options = {}) {
|
|
|
8725
7884
|
? { decompositionMatch: err.runtimeActionDecompositionMatch }
|
|
8726
7885
|
: {}),
|
|
8727
7886
|
...(err?.runtimeActionInferenceTrace ? { inferenceTrace: err.runtimeActionInferenceTrace } : {}),
|
|
8728
|
-
...(readString(err?.runtimeActionFieldPath) ? { fieldPath: readString(err.runtimeActionFieldPath) } : {}),
|
|
8729
|
-
...(readString(err?.runtimeActionReceivedType) ? { receivedType: readString(err.runtimeActionReceivedType) } : {}),
|
|
8730
|
-
...(readString(err?.runtimeActionRequiredAuthorization) ? { requiredAuthorization: readString(err.runtimeActionRequiredAuthorization) } : {}),
|
|
8731
|
-
...(readString(err?.runtimeActionAuthorizationSourceKind) ? { authorizationSourceKind: readString(err.runtimeActionAuthorizationSourceKind) } : {}),
|
|
8732
|
-
...(readString(err?.runtimeActionAuthorizationSourceId) ? { authorizationSourceId: readString(err.runtimeActionAuthorizationSourceId) } : {}),
|
|
8733
|
-
...(readString(err?.runtimeActionAuthorizationSourceRevision) ? { authorizationSourceRevision: readString(err.runtimeActionAuthorizationSourceRevision) } : {}),
|
|
8734
7887
|
...(Array.isArray(err?.runtimeActionAllowedContentTypes)
|
|
8735
7888
|
? { allowedContentTypes: err.runtimeActionAllowedContentTypes }
|
|
8736
7889
|
: {}),
|
|
@@ -8740,7 +7893,6 @@ function runtimeActionDiagnostics(err, options = {}) {
|
|
|
8740
7893
|
...(err?.runtimeActionVerificationResult
|
|
8741
7894
|
? { verificationResult: err.runtimeActionVerificationResult }
|
|
8742
7895
|
: {}),
|
|
8743
|
-
...(err?.runtimeActionLedger ? { actionLedger: err.runtimeActionLedger } : {}),
|
|
8744
7896
|
...(runtimeActionExpectedShape(actionType, actionKind) ? { expectedShape: runtimeActionExpectedShape(actionType, actionKind) } : {}),
|
|
8745
7897
|
...(typeof err?.httpStatus === "number" ? { backendStatus: err.httpStatus } : {}),
|
|
8746
7898
|
...(backendErrors.length > 0 ? { backendErrors } : {}),
|
|
@@ -8775,14 +7927,6 @@ function classifyRuntimeActionFailure(err) {
|
|
|
8775
7927
|
...(diagnostics ? { runtimeActionDiagnostics: diagnostics } : {}),
|
|
8776
7928
|
};
|
|
8777
7929
|
}
|
|
8778
|
-
if (actionType === "output_channel") {
|
|
8779
|
-
return {
|
|
8780
|
-
errorCode: readString(err?.runtimeActionErrorCode) ?? "runtime_action_validation_failed",
|
|
8781
|
-
errorFamily: "output_contract",
|
|
8782
|
-
...details,
|
|
8783
|
-
...(diagnostics ? { runtimeActionDiagnostics: diagnostics } : {}),
|
|
8784
|
-
};
|
|
8785
|
-
}
|
|
8786
7930
|
if (
|
|
8787
7931
|
actionType === "create_interaction" &&
|
|
8788
7932
|
/create_interaction\.[a-z_]+ action requires payload|create_interaction action requires payload|non-canonical fields/i.test(haystack)
|
|
@@ -8794,10 +7938,7 @@ function classifyRuntimeActionFailure(err) {
|
|
|
8794
7938
|
...(diagnostics ? { runtimeActionDiagnostics: diagnostics } : {}),
|
|
8795
7939
|
};
|
|
8796
7940
|
}
|
|
8797
|
-
if (
|
|
8798
|
-
actionType === "upload_artifact" &&
|
|
8799
|
-
/unsupported artifact content type|requires path|file does not exist|path must be inside/i.test(haystack)
|
|
8800
|
-
) {
|
|
7941
|
+
if (actionType === "upload_artifact" && /unsupported artifact content type/i.test(haystack)) {
|
|
8801
7942
|
return {
|
|
8802
7943
|
errorCode: "runtime_action_validation_failed",
|
|
8803
7944
|
errorFamily: "output_contract",
|
|
@@ -8821,31 +7962,6 @@ function classifyRuntimeActionFailure(err) {
|
|
|
8821
7962
|
...(diagnostics ? { runtimeActionDiagnostics: diagnostics } : {}),
|
|
8822
7963
|
};
|
|
8823
7964
|
}
|
|
8824
|
-
if (/runtime_action_postcondition_failed/i.test(haystack)) {
|
|
8825
|
-
return {
|
|
8826
|
-
errorCode: "runtime_action_postcondition_failed",
|
|
8827
|
-
errorFamily: "output_contract",
|
|
8828
|
-
...details,
|
|
8829
|
-
...(diagnostics ? { runtimeActionDiagnostics: diagnostics } : {}),
|
|
8830
|
-
};
|
|
8831
|
-
}
|
|
8832
|
-
if (err?.httpStatus === 401) {
|
|
8833
|
-
const authenticationDiagnostics = runtimeActionDiagnostics(err, { retryable: true });
|
|
8834
|
-
return {
|
|
8835
|
-
errorCode: "runtime_action_authentication_failed",
|
|
8836
|
-
errorFamily: "authentication",
|
|
8837
|
-
...details,
|
|
8838
|
-
...(authenticationDiagnostics ? { runtimeActionDiagnostics: authenticationDiagnostics } : {}),
|
|
8839
|
-
};
|
|
8840
|
-
}
|
|
8841
|
-
if (err?.httpStatus === 403) {
|
|
8842
|
-
return {
|
|
8843
|
-
errorCode: "runtime_action_permission_denied",
|
|
8844
|
-
errorFamily: "permission",
|
|
8845
|
-
...details,
|
|
8846
|
-
...(diagnostics ? { runtimeActionDiagnostics: diagnostics } : {}),
|
|
8847
|
-
};
|
|
8848
|
-
}
|
|
8849
7965
|
if (/approval-gated executable child tasks/i.test(haystack)) {
|
|
8850
7966
|
return {
|
|
8851
7967
|
errorCode: "runtime_action_validation_failed",
|
|
@@ -8905,27 +8021,6 @@ function piOutputUsageMetadataMissing(parsed) {
|
|
|
8905
8021
|
return totalTokens <= 0;
|
|
8906
8022
|
}
|
|
8907
8023
|
|
|
8908
|
-
function runtimeBusinessDisposition(runtimeActions, inferredArtifactUploads) {
|
|
8909
|
-
const results = Array.isArray(runtimeActions?.results)
|
|
8910
|
-
? runtimeActions.results.map(asRecord).filter((result) => !isSkippedRuntimeActionResult(result))
|
|
8911
|
-
: [];
|
|
8912
|
-
const terminalUpdate = results.find((result) =>
|
|
8913
|
-
readString(result.type) === "update_parent" &&
|
|
8914
|
-
["done", "cancelled", "blocked", "in_review"].includes(readString(result.status) ?? ""));
|
|
8915
|
-
const terminalStatus = readString(terminalUpdate?.status);
|
|
8916
|
-
if (["done", "cancelled"].includes(terminalStatus ?? "")) {
|
|
8917
|
-
return { state: "completed", reason: `parent_${terminalStatus}` };
|
|
8918
|
-
}
|
|
8919
|
-
if (terminalStatus === "blocked") return { state: "blocked", reason: "parent_blocked" };
|
|
8920
|
-
if (terminalStatus === "in_review" || results.some((result) => readString(result.type) === "create_interaction")) {
|
|
8921
|
-
return { state: "waiting", reason: "review_or_interaction_path_recorded" };
|
|
8922
|
-
}
|
|
8923
|
-
if (results.length > 0 || readNumber(inferredArtifactUploads?.appliedCount, 0) > 0) {
|
|
8924
|
-
return { state: "needs_followup", reason: "durable_progress_without_terminal_disposition" };
|
|
8925
|
-
}
|
|
8926
|
-
return { state: "missing", reason: "no_durable_action_or_live_path" };
|
|
8927
|
-
}
|
|
8928
|
-
|
|
8929
8024
|
function piCompletionOutputStopped(parsed, execution) {
|
|
8930
8025
|
return execution?.timedOut !== true
|
|
8931
8026
|
&& execution?.signal === "SIGTERM"
|
|
@@ -9163,27 +8258,9 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
|
|
|
9163
8258
|
...executorActionSources,
|
|
9164
8259
|
].filter(Boolean).join("\n\n");
|
|
9165
8260
|
const envelope = extractRuntimeActionEnvelope(actionText);
|
|
9166
|
-
if (!envelope && executorKind === "pi" && parsed.runtimeActionMarkerInToolOutput === true) {
|
|
9167
|
-
const err = runtimeActionValidationError(
|
|
9168
|
-
"AMASTER_RUNTIME_ACTIONS must be emitted in a Pi assistant message; a marker was found only in tool output, and tool output is ignored by the runtime action bridge",
|
|
9169
|
-
);
|
|
9170
|
-
err.runtimeActionType = "output_channel";
|
|
9171
|
-
err.runtimeActionReasonHint = "assistant_message_required";
|
|
9172
|
-
throw err;
|
|
9173
|
-
}
|
|
9174
|
-
if (!envelope && hasMalformedRuntimeActionMarkerIntent(actionText)) {
|
|
9175
|
-
const err = runtimeActionValidationError(
|
|
9176
|
-
"AMASTER_RUNTIME_ACTIONS action intent used a non-canonical marker; the marker must be an unformatted bare line",
|
|
9177
|
-
);
|
|
9178
|
-
err.runtimeActionType = "output_channel";
|
|
9179
|
-
err.runtimeActionReasonHint = "bare_marker_line_required";
|
|
9180
|
-
err.runtimeActionErrorCode = "runtime_action_output_marker_invalid";
|
|
9181
|
-
throw err;
|
|
9182
|
-
}
|
|
9183
8261
|
if (!envelope) return null;
|
|
9184
8262
|
const rawActions = Array.isArray(envelope.actions) ? envelope.actions.map(asRecord) : [];
|
|
9185
8263
|
const continuationScope = runtimeActionContinuationScope(command);
|
|
9186
|
-
const authorizationEnvelope = runtimeActionAuthorizationEnvelope(command);
|
|
9187
8264
|
assertRuntimeActionsAllowedByContinuationScope(rawActions, continuationScope);
|
|
9188
8265
|
assertExplicitTaskDecomposition(command, rawActions);
|
|
9189
8266
|
if (
|
|
@@ -9196,10 +8273,6 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
|
|
|
9196
8273
|
const runtimeAuth = runtimeActionAuthForCommand(command);
|
|
9197
8274
|
const issueId = readString(runtimeAuth.issueId) ?? commandIssueId(command);
|
|
9198
8275
|
if (!issueId) throw new Error("AMaster runtime action bridge requires a current issue id");
|
|
9199
|
-
const batchFingerprint = `runtime-action-batch:${stableRuntimeActionHash({
|
|
9200
|
-
runId: runtimeActionRunId(runtimeAuth) ?? commandRunId(command),
|
|
9201
|
-
envelope,
|
|
9202
|
-
})}`;
|
|
9203
8276
|
const allowedContentTypes = runtimeAttachmentContentTypes(command);
|
|
9204
8277
|
let deliverableManifest;
|
|
9205
8278
|
try {
|
|
@@ -9231,41 +8304,20 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
|
|
|
9231
8304
|
attachRuntimeCheckpointCandidates(err, envelope, cwd);
|
|
9232
8305
|
throw err;
|
|
9233
8306
|
}
|
|
9234
|
-
|
|
8307
|
+
const actions = orderRuntimeActionsForGovernance([...generatedUploadActions, ...rawActions]);
|
|
9235
8308
|
const answeredQuestionContinuation = runtimeActionAnsweredQuestionContinuation(command);
|
|
9236
|
-
const acceptedConfirmationContinuation = runtimeActionAcceptedConfirmationContinuation(command);
|
|
9237
8309
|
try {
|
|
9238
|
-
|
|
8310
|
+
preflightRuntimeActionBatch(actions, {
|
|
9239
8311
|
answeredQuestionContinuation,
|
|
9240
|
-
acceptedConfirmationContinuation,
|
|
9241
8312
|
allowedContentTypes,
|
|
9242
8313
|
continuationScope,
|
|
9243
|
-
cwd,
|
|
9244
8314
|
});
|
|
9245
|
-
assertRuntimeActionsAllowedByAuthorizationEnvelope(actions, authorizationEnvelope);
|
|
9246
8315
|
} catch (err) {
|
|
9247
|
-
if (err && typeof err === "object" && !err.runtimeActionLedger) {
|
|
9248
|
-
const fieldPath = readString(err.runtimeActionFieldPath);
|
|
9249
|
-
const failedIndex = Number.parseInt(fieldPath?.match(/^actions\[(\d+)]/)?.[1] ?? "0", 10);
|
|
9250
|
-
err.runtimeActionLedger = {
|
|
9251
|
-
batchFingerprint,
|
|
9252
|
-
traceId: batchFingerprint,
|
|
9253
|
-
status: "validation_failed",
|
|
9254
|
-
actions: actions.map((action, index) => runtimeActionLedgerEntry(
|
|
9255
|
-
action,
|
|
9256
|
-
index,
|
|
9257
|
-
index === failedIndex ? "failed" : "pending",
|
|
9258
|
-
null,
|
|
9259
|
-
index === failedIndex ? err : null,
|
|
9260
|
-
)),
|
|
9261
|
-
};
|
|
9262
|
-
}
|
|
9263
8316
|
attachRuntimeCheckpointCandidates(err, envelope, cwd);
|
|
9264
8317
|
throw err;
|
|
9265
8318
|
}
|
|
9266
8319
|
const results = [];
|
|
9267
8320
|
const skipped = [];
|
|
9268
|
-
const actionLedgerEntries = [];
|
|
9269
8321
|
const actionContext = {
|
|
9270
8322
|
rootIssueId: issueId,
|
|
9271
8323
|
cwd,
|
|
@@ -9275,20 +8327,14 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
|
|
|
9275
8327
|
createdChildIssueIdByFingerprint: new Map(),
|
|
9276
8328
|
completedChildIssuesByTitle: runtimeActionCompletedChildIssueMap(command),
|
|
9277
8329
|
createdReviewPathForInReview: false,
|
|
9278
|
-
acceptedConfirmationContinuation,
|
|
8330
|
+
acceptedConfirmationContinuation: runtimeActionAcceptedConfirmationContinuation(command),
|
|
9279
8331
|
answeredQuestionContinuation,
|
|
9280
8332
|
finalCommentBodies: [],
|
|
9281
8333
|
};
|
|
9282
|
-
for (const
|
|
8334
|
+
for (const action of actions) {
|
|
9283
8335
|
try {
|
|
9284
8336
|
const result = await applyRuntimeAction(config, command, runtimeAuth, issueId, action, actionContext);
|
|
9285
8337
|
results.push(result);
|
|
9286
|
-
actionLedgerEntries.push(runtimeActionLedgerEntry(
|
|
9287
|
-
action,
|
|
9288
|
-
actionIndex,
|
|
9289
|
-
asRecord(result).reusedExisting === true ? "reused" : "applied",
|
|
9290
|
-
result,
|
|
9291
|
-
));
|
|
9292
8338
|
if (isSkippedRuntimeActionResult(result)) {
|
|
9293
8339
|
skipped.push(result);
|
|
9294
8340
|
}
|
|
@@ -9317,17 +8363,6 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
|
|
|
9317
8363
|
err.runtimeActionOriginalType = readRuntimeActionOriginalType(action);
|
|
9318
8364
|
err.runtimeActionKind = readString(action.kind) ?? readString(asRecord(action.payload).kind);
|
|
9319
8365
|
err.runtimeActionReason = truncateText(err instanceof Error ? err.message : String(err), 1000);
|
|
9320
|
-
err.runtimeActionLedger = {
|
|
9321
|
-
batchFingerprint,
|
|
9322
|
-
traceId: batchFingerprint,
|
|
9323
|
-
status: "partial_failed",
|
|
9324
|
-
actions: [
|
|
9325
|
-
...actionLedgerEntries,
|
|
9326
|
-
runtimeActionLedgerEntry(action, actionIndex, "failed", null, err),
|
|
9327
|
-
...actions.slice(actionIndex + 1).map((pendingAction, offset) =>
|
|
9328
|
-
runtimeActionLedgerEntry(pendingAction, actionIndex + offset + 1, "pending")),
|
|
9329
|
-
],
|
|
9330
|
-
};
|
|
9331
8366
|
}
|
|
9332
8367
|
attachRuntimeCheckpointCandidates(err, envelope, cwd);
|
|
9333
8368
|
throw err;
|
|
@@ -9335,7 +8370,6 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
|
|
|
9335
8370
|
const skippedResult = runtimeActionFailureSummary(action, err);
|
|
9336
8371
|
skipped.push(skippedResult);
|
|
9337
8372
|
results.push(skippedResult);
|
|
9338
|
-
actionLedgerEntries.push(runtimeActionLedgerEntry(action, actionIndex, "skipped", skippedResult, err));
|
|
9339
8373
|
await ingestLog(config, command, "system", "warn", `Skipped AMaster runtime action: ${skippedResult.reason}`, {
|
|
9340
8374
|
runtimeAction: skippedResult,
|
|
9341
8375
|
});
|
|
@@ -9343,19 +8377,8 @@ async function applyRuntimeActionBridge(config, command, parsed, execution, cwd)
|
|
|
9343
8377
|
}
|
|
9344
8378
|
return {
|
|
9345
8379
|
version: readNumber(envelope.version, 1),
|
|
9346
|
-
markerFound: true,
|
|
9347
|
-
jsonParsed: true,
|
|
9348
|
-
actionsValidated: true,
|
|
9349
|
-
postconditionsPassed: true,
|
|
9350
|
-
actionLedger: {
|
|
9351
|
-
batchFingerprint,
|
|
9352
|
-
traceId: batchFingerprint,
|
|
9353
|
-
status: "completed",
|
|
9354
|
-
actions: actionLedgerEntries,
|
|
9355
|
-
},
|
|
9356
8380
|
requestedCount: actions.length,
|
|
9357
8381
|
appliedCount: results.length - skipped.length,
|
|
9358
|
-
actionsApplied: results.length - skipped.length,
|
|
9359
8382
|
skippedCount: skipped.length,
|
|
9360
8383
|
results,
|
|
9361
8384
|
deliverableManifest: {
|
|
@@ -9386,72 +8409,6 @@ async function listExistingIssueWorkProducts(runtimeAuth, issueId) {
|
|
|
9386
8409
|
}
|
|
9387
8410
|
}
|
|
9388
8411
|
|
|
9389
|
-
async function verifyUploadedArtifactPostcondition(runtimeAuth, issueId, expected, fields) {
|
|
9390
|
-
const [workProducts, attachments] = await Promise.all([
|
|
9391
|
-
runtimeApiJson(runtimeAuth, "GET", `/api/issues/${encodeURIComponent(issueId)}/work-products`),
|
|
9392
|
-
runtimeApiJson(runtimeAuth, "GET", `/api/issues/${encodeURIComponent(issueId)}/attachments`),
|
|
9393
|
-
]);
|
|
9394
|
-
const workProduct = Array.isArray(workProducts)
|
|
9395
|
-
? workProducts.map(asRecord).find((entry) => readString(entry.id) === expected.workProductId)
|
|
9396
|
-
: null;
|
|
9397
|
-
const attachment = Array.isArray(attachments)
|
|
9398
|
-
? attachments.map(asRecord).find((entry) => readString(entry.id) === expected.attachmentId)
|
|
9399
|
-
: null;
|
|
9400
|
-
const metadata = asRecord(workProduct?.metadata);
|
|
9401
|
-
const expectedSourceIds = Array.isArray(fields.sourceWorkProductIds)
|
|
9402
|
-
? fields.sourceWorkProductIds.map(readString).filter(Boolean).sort()
|
|
9403
|
-
: [];
|
|
9404
|
-
const persistedSourceIds = Array.isArray(metadata.sourceWorkProductIds)
|
|
9405
|
-
? metadata.sourceWorkProductIds.map(readString).filter(Boolean).sort()
|
|
9406
|
-
: [];
|
|
9407
|
-
const expectedScenarioKeys = Array.isArray(fields.scenarioKeys)
|
|
9408
|
-
? fields.scenarioKeys.map(readString).filter(Boolean).sort()
|
|
9409
|
-
: [];
|
|
9410
|
-
const persistedScenarioKeys = Array.isArray(metadata.scenarioKeys)
|
|
9411
|
-
? metadata.scenarioKeys.map(readString).filter(Boolean).sort()
|
|
9412
|
-
: [];
|
|
9413
|
-
const failures = [
|
|
9414
|
-
!workProduct ? "work product missing" : null,
|
|
9415
|
-
!attachment ? "attachment missing" : null,
|
|
9416
|
-
readString(metadata.attachmentId) !== expected.attachmentId ? "work product attachmentId mismatch" : null,
|
|
9417
|
-
readString(metadata.sha256) !== expected.sha256 ? "work product SHA mismatch" : null,
|
|
9418
|
-
readString(attachment?.sha256) !== expected.sha256 ? "attachment SHA mismatch" : null,
|
|
9419
|
-
readString(metadata.sourceWorkProductId) !== readString(fields.sourceWorkProductId) ? "sourceWorkProductId mismatch" : null,
|
|
9420
|
-
readString(metadata.supersedesWorkProductId) !== readString(fields.supersedesWorkProductId) ? "supersedesWorkProductId mismatch" : null,
|
|
9421
|
-
readString(metadata.evidenceRole) !== readString(fields.evidenceRole) ? "evidenceRole mismatch" : null,
|
|
9422
|
-
JSON.stringify(persistedSourceIds) !== JSON.stringify(expectedSourceIds) ? "sourceWorkProductIds mismatch" : null,
|
|
9423
|
-
JSON.stringify(persistedScenarioKeys) !== JSON.stringify(expectedScenarioKeys) ? "scenarioKeys mismatch" : null,
|
|
9424
|
-
].filter(Boolean);
|
|
9425
|
-
if (failures.length > 0) {
|
|
9426
|
-
throw new Error(`runtime_action_postcondition_failed: artifact upload readback failed: ${failures.join("; ")}`);
|
|
9427
|
-
}
|
|
9428
|
-
}
|
|
9429
|
-
|
|
9430
|
-
async function cleanupFailedArtifactUpload(config, command, runtimeAuth, attachmentId, workProductId, cause) {
|
|
9431
|
-
const cleanupFailures = [];
|
|
9432
|
-
if (workProductId) {
|
|
9433
|
-
try {
|
|
9434
|
-
await runtimeApiJson(runtimeAuth, "DELETE", `/api/work-products/${encodeURIComponent(workProductId)}`);
|
|
9435
|
-
} catch (err) {
|
|
9436
|
-
cleanupFailures.push(`workProduct=${err instanceof Error ? err.message : String(err)}`);
|
|
9437
|
-
}
|
|
9438
|
-
}
|
|
9439
|
-
try {
|
|
9440
|
-
await runtimeApiJson(runtimeAuth, "DELETE", `/api/attachments/${encodeURIComponent(attachmentId)}`);
|
|
9441
|
-
} catch (err) {
|
|
9442
|
-
cleanupFailures.push(`attachment=${err instanceof Error ? err.message : String(err)}`);
|
|
9443
|
-
}
|
|
9444
|
-
await ingestLog(config, command, "system", "error", "Artifact upload postcondition failed", {
|
|
9445
|
-
attachmentId,
|
|
9446
|
-
workProductId: workProductId ?? null,
|
|
9447
|
-
cause: cause instanceof Error ? cause.message : String(cause),
|
|
9448
|
-
cleanupFailures,
|
|
9449
|
-
});
|
|
9450
|
-
if (cleanupFailures.length > 0) {
|
|
9451
|
-
throw new Error(`${cause instanceof Error ? cause.message : String(cause)}; cleanup failed: ${cleanupFailures.join("; ")}`);
|
|
9452
|
-
}
|
|
9453
|
-
}
|
|
9454
|
-
|
|
9455
8412
|
function existingArtifactUploadMatches(artifact, existingWorkProducts) {
|
|
9456
8413
|
const filename = basename(artifact.path);
|
|
9457
8414
|
const byteSize = statSync(artifact.path).size;
|
|
@@ -9477,7 +8434,7 @@ function existingArtifactUploadMatches(artifact, existingWorkProducts) {
|
|
|
9477
8434
|
});
|
|
9478
8435
|
}
|
|
9479
8436
|
|
|
9480
|
-
function existingExplicitArtifactUploadMatch(filePath, title, existingWorkProducts
|
|
8437
|
+
function existingExplicitArtifactUploadMatch(filePath, title, existingWorkProducts) {
|
|
9481
8438
|
const filename = basename(filePath);
|
|
9482
8439
|
const byteSize = statSync(filePath).size;
|
|
9483
8440
|
const sha256 = hashFileSha256(filePath);
|
|
@@ -9488,22 +8445,6 @@ function existingExplicitArtifactUploadMatch(filePath, title, existingWorkProduc
|
|
|
9488
8445
|
if (readString(workProduct.provider) !== "paperclip") return false;
|
|
9489
8446
|
if (readString(workProduct.status) === "archived") return false;
|
|
9490
8447
|
const metadata = asRecord(workProduct.metadata);
|
|
9491
|
-
const sourceWorkProductId = readString(fields.sourceWorkProductId);
|
|
9492
|
-
const sourceWorkProductIds = Array.isArray(fields.sourceWorkProductIds)
|
|
9493
|
-
? fields.sourceWorkProductIds.map(readString).filter(Boolean).sort()
|
|
9494
|
-
: [];
|
|
9495
|
-
const supersedesWorkProductId = readString(fields.supersedesWorkProductId);
|
|
9496
|
-
const evidenceRole = readString(fields.evidenceRole);
|
|
9497
|
-
const scenarioKeys = Array.isArray(fields.scenarioKeys) ? fields.scenarioKeys.map(readString).filter(Boolean).sort() : [];
|
|
9498
|
-
if (sourceWorkProductId || supersedesWorkProductId || sourceWorkProductIds.length > 0 || evidenceRole || scenarioKeys.length > 0) {
|
|
9499
|
-
if (readString(metadata.sourceWorkProductId) !== sourceWorkProductId) return false;
|
|
9500
|
-
if (readString(metadata.supersedesWorkProductId) !== supersedesWorkProductId) return false;
|
|
9501
|
-
const existingSourceIds = Array.isArray(metadata.sourceWorkProductIds) ? metadata.sourceWorkProductIds.map(readString).filter(Boolean).sort() : [];
|
|
9502
|
-
const existingScenarioKeys = Array.isArray(metadata.scenarioKeys) ? metadata.scenarioKeys.map(readString).filter(Boolean).sort() : [];
|
|
9503
|
-
if (JSON.stringify(existingSourceIds) !== JSON.stringify(sourceWorkProductIds)) return false;
|
|
9504
|
-
if (readString(metadata.evidenceRole) !== evidenceRole) return false;
|
|
9505
|
-
if (JSON.stringify(existingScenarioKeys) !== JSON.stringify(scenarioKeys)) return false;
|
|
9506
|
-
}
|
|
9507
8448
|
const existingSha = readString(metadata.sha256) ?? readString(workProduct.sha256);
|
|
9508
8449
|
if (existingSha) return existingSha === sha256;
|
|
9509
8450
|
|
|
@@ -9626,10 +8567,9 @@ async function executeRunCommand(config, command) {
|
|
|
9626
8567
|
});
|
|
9627
8568
|
}
|
|
9628
8569
|
await materializeIssueCheckpoint(config, command, workspace);
|
|
9629
|
-
|
|
9630
|
-
let materializedAttachments = requiredArtifactInputs;
|
|
8570
|
+
let materializedAttachments = [];
|
|
9631
8571
|
try {
|
|
9632
|
-
materializedAttachments =
|
|
8572
|
+
materializedAttachments = await materializeIssueAttachments(config, command, workspace);
|
|
9633
8573
|
} catch (err) {
|
|
9634
8574
|
await ingestLog(config, command, "system", "warn", `Failed to materialize issue attachments: ${err instanceof Error ? err.message : String(err)}`, {
|
|
9635
8575
|
error: err instanceof Error ? err.message : String(err),
|
|
@@ -9854,7 +8794,6 @@ async function executeRunCommand(config, command) {
|
|
|
9854
8794
|
? "Executor cancelled by AMaster control plane"
|
|
9855
8795
|
: execution.spawnError ?? parsedErrorMessage ?? runtimeActionError ?? inferredArtifactError ?? (succeeded ? null : `Executor exited with code ${execution.exitCode ?? "unknown"}`);
|
|
9856
8796
|
const artifactUploadSummary = runtimeArtifactUploadSummary(runtimeActions, inferredArtifactUploads);
|
|
9857
|
-
const businessDisposition = runtimeBusinessDisposition(runtimeActions, inferredArtifactUploads);
|
|
9858
8797
|
const costUsage = parsedCostUsage(parsed.usage);
|
|
9859
8798
|
const result = {
|
|
9860
8799
|
executorKind: executor.kind,
|
|
@@ -9925,7 +8864,6 @@ async function executeRunCommand(config, command) {
|
|
|
9925
8864
|
...(runtimeActions ? { runtimeActions } : {}),
|
|
9926
8865
|
...(inferredArtifactUploads ? { inferredArtifactUploads } : {}),
|
|
9927
8866
|
...(artifactUploadSummary ? { artifactUploadSummary } : {}),
|
|
9928
|
-
businessDisposition,
|
|
9929
8867
|
};
|
|
9930
8868
|
const completion = await completeCommand(config, command, cancelled ? "cancelled" : succeeded ? "succeeded" : "failed", result, error ?? undefined);
|
|
9931
8869
|
if (!completion) {
|
|
@@ -10120,26 +9058,6 @@ function statusSummary(config) {
|
|
|
10120
9058
|
}), null, 2)}\n`);
|
|
10121
9059
|
}
|
|
10122
9060
|
|
|
10123
|
-
function validateRuntimeActionsFile(config, flags) {
|
|
10124
|
-
const rawFile = readString(flags.file);
|
|
10125
|
-
if (!rawFile) throw new Error("--file is required");
|
|
10126
|
-
const cwd = resolve(readString(flags.cwd) ?? process.cwd());
|
|
10127
|
-
const file = resolve(cwd, rawFile);
|
|
10128
|
-
const allowedContentTypes = splitList(flags["allowed-content-types"]);
|
|
10129
|
-
const envelope = JSON.parse(readFileSync(file, "utf8"));
|
|
10130
|
-
if (!envelope || typeof envelope !== "object" || Array.isArray(envelope)) {
|
|
10131
|
-
throw runtimeActionValidationError("runtime action envelope must be a JSON object");
|
|
10132
|
-
}
|
|
10133
|
-
assertRuntimeActionValid(envelope.version === 1, "runtime action envelope version must be 1");
|
|
10134
|
-
assertRuntimeActionValid(Array.isArray(envelope.actions), "runtime action envelope actions must be an array");
|
|
10135
|
-
assertRuntimeActionValid(envelope.actions.length <= 20, "runtime action envelope actions exceed the limit of 20");
|
|
10136
|
-
|
|
10137
|
-
preflightRuntimeDeliverables(config, envelope, cwd, allowedContentTypes);
|
|
10138
|
-
const actions = orderRuntimeActionsForGovernance(envelope.actions.map(asRecord));
|
|
10139
|
-
preflightRuntimeActionBatch(actions, { cwd, allowedContentTypes });
|
|
10140
|
-
process.stdout.write(`${JSON.stringify({ valid: true, actionCount: actions.length }, null, 2)}\n`);
|
|
10141
|
-
}
|
|
10142
|
-
|
|
10143
9061
|
async function runLoop(config) {
|
|
10144
9062
|
config = await ensureRegistered(config);
|
|
10145
9063
|
let startupOrphanCheckPending = true;
|
|
@@ -10173,7 +9091,6 @@ Commands:
|
|
|
10173
9091
|
run-once Register if needed, heartbeat, poll once, execute leased commands, then exit.
|
|
10174
9092
|
workspace-gc-dry-run Print managed runtime workdir GC candidates without deleting anything.
|
|
10175
9093
|
status-summary Print safe local daemon storage/outbox summary JSON.
|
|
10176
|
-
validate-actions Validate a JSON action envelope with --file, --cwd, and --allowed-content-types.
|
|
10177
9094
|
start | run Register if needed, then heartbeat and poll in a foreground loop.
|
|
10178
9095
|
|
|
10179
9096
|
Key env:
|
|
@@ -10225,9 +9142,6 @@ async function main() {
|
|
|
10225
9142
|
case "status-summary":
|
|
10226
9143
|
statusSummary(config);
|
|
10227
9144
|
break;
|
|
10228
|
-
case "validate-actions":
|
|
10229
|
-
validateRuntimeActionsFile(config, flags);
|
|
10230
|
-
break;
|
|
10231
9145
|
case "start":
|
|
10232
9146
|
case "run":
|
|
10233
9147
|
await runLoop(config);
|