agentxchain 2.155.13 → 2.155.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentxchain",
3
- "version": "2.155.13",
3
+ "version": "2.155.15",
4
4
  "description": "CLI for AgentXchain — governed multi-agent software delivery",
5
5
  "type": "module",
6
6
  "bin": {
@@ -529,6 +529,12 @@ function renderPrompt(role, roleId, turn, state, config, root) {
529
529
  lines.push(`- **You are in the \`${currentPhase}\` phase.** When your work is complete${gateClause}, set \`phase_transition_request: "${nextPhase}"\` to advance to the next phase.`);
530
530
  } else if (phaseIdx === phaseNames.length - 1) {
531
531
  lines.push(`- **You are in the \`${currentPhase}\` phase (final phase).** When ready to ship, set \`run_completion_request: true\` and \`phase_transition_request: null\`.`);
532
+ if (runCompletionAutoApprovalApplies(config)) {
533
+ lines.push('- Run completion is governed by `approval_policy.run_completion.action: "auto_approve"` for this run.');
534
+ lines.push('- Do NOT set `status: "needs_human"` solely to request final run approval. If the required artifacts are complete and there are no genuine blockers, set `status: "completed"` with `run_completion_request: true`; the orchestrator will evaluate and auto-approve completion.');
535
+ } else {
536
+ lines.push('- If final approval is human-gated, set `run_completion_request: true` when ready; the orchestrator will route the approval gate.');
537
+ }
532
538
  }
533
539
  }
534
540
  // Phase-specific guidance for proposed roles
@@ -542,6 +548,12 @@ function renderPrompt(role, roleId, turn, state, config, root) {
542
548
  lines.push(`- **You are in the \`${currentPhase}\` phase (not final phase).** When your work is complete${gateClause}, set \`phase_transition_request: "${nextPhase}"\`.`);
543
549
  } else if (phaseIdx >= 0 && phaseIdx === phaseNames.length - 1) {
544
550
  lines.push(`- **You are in the \`${currentPhase}\` phase (final phase).** When ready to ship, set \`run_completion_request: true\` and \`phase_transition_request: null\`.`);
551
+ if (runCompletionAutoApprovalApplies(config)) {
552
+ lines.push('- Run completion is governed by `approval_policy.run_completion.action: "auto_approve"` for this run.');
553
+ lines.push('- Do NOT set `status: "needs_human"` solely to request final run approval. If the required artifacts are complete and there are no genuine blockers, set `status: "completed"` with `run_completion_request: true`; the orchestrator will evaluate and auto-approve completion.');
554
+ } else {
555
+ lines.push('- If final approval is human-gated, set `run_completion_request: true` when ready; the orchestrator will route the approval gate.');
556
+ }
545
557
  if (runtimeType === 'api_proxy' || runtimeType === 'remote_agent') {
546
558
  lines.push('- **Completion turns must be no-op:** set `proposed_changes` to `[]` or omit it, set `files_changed` to `[]`, and set `artifact.type` to `"review"`. Do NOT propose file changes on a completion turn.');
547
559
  }
@@ -5023,17 +5023,21 @@ function _acceptGovernedTurnLocked(root, config, opts) {
5023
5023
  const remainingReservations = { ...(state.budget_reservations || {}) };
5024
5024
  delete remainingReservations[currentTurn.turn_id];
5025
5025
  const costUsd = turnResult.cost?.usd || 0;
5026
+ const isIdleExpansionNewIntake = idleExpansionResultSummary?.kind === 'new_intake_intent';
5027
+ const suppressIdleExpansionNeedsHuman = isIdleExpansionNewIntake && turnResult.status === 'needs_human';
5026
5028
  let updatedState = {
5027
5029
  ...state,
5028
5030
  turn_sequence: acceptedSequence,
5029
5031
  last_completed_turn_id: currentTurn.turn_id,
5030
5032
  active_turns: remainingTurns,
5031
5033
  budget_reservations: remainingReservations,
5032
- blocked_on: turnResult.status === 'needs_human' ? `human:${turnResult.needs_human_reason || 'unspecified'}` : null,
5034
+ blocked_on: turnResult.status === 'needs_human' && !suppressIdleExpansionNeedsHuman ? `human:${turnResult.needs_human_reason || 'unspecified'}` : null,
5033
5035
  blocked_reason: null,
5034
5036
  escalation: null,
5035
5037
  accepted_integration_ref: derivedRef,
5036
- next_recommended_role: deriveNextRecommendedRole(turnResult, state, config),
5038
+ next_recommended_role: suppressIdleExpansionNeedsHuman
5039
+ ? currentTurn.assigned_role
5040
+ : deriveNextRecommendedRole(turnResult, state, config),
5037
5041
  budget_status: {
5038
5042
  ...(state.budget_status || {}),
5039
5043
  spent_usd: (state.budget_status?.spent_usd || 0) + costUsd,
@@ -5153,7 +5157,7 @@ function _acceptGovernedTurnLocked(root, config, opts) {
5153
5157
  updatedState.escalation = null;
5154
5158
  }
5155
5159
 
5156
- if (turnResult.status === 'needs_human') {
5160
+ if (turnResult.status === 'needs_human' && !suppressIdleExpansionNeedsHuman) {
5157
5161
  updatedState.status = 'blocked';
5158
5162
  updatedState.blocked_reason = buildBlockedReason({
5159
5163
  category: 'needs_human',
@@ -5223,11 +5227,11 @@ function _acceptGovernedTurnLocked(root, config, opts) {
5223
5227
  let completionResult = null;
5224
5228
  let timeoutResult = null;
5225
5229
 
5226
- // BUG-70: idle-expansion new_intake_intent is a proposal, not a chartered plan.
5227
- // Suppress phase_transition_request the new intake must be materialized into
5228
- // planning artifacts by a subsequent turn before implementation can proceed.
5229
- const isIdleExpansionNewIntake = idleExpansionResultSummary?.kind === 'new_intake_intent';
5230
- if (isIdleExpansionNewIntake && turnResult.phase_transition_request) {
5230
+ // BUG-70/71: idle-expansion new_intake_intent is a proposal, not a chartered plan.
5231
+ // Suppress phase_transition_request and human-only intake approval escalations
5232
+ // the new intake must be materialized into planning artifacts by a subsequent
5233
+ // planning turn before implementation can proceed.
5234
+ if (isIdleExpansionNewIntake && (turnResult.phase_transition_request || suppressIdleExpansionNeedsHuman)) {
5231
5235
  const rawIdleResult = turnResult.idle_expansion_result;
5232
5236
  updatedState.charter_materialization_pending = {
5233
5237
  charter: rawIdleResult?.new_intake_intent?.charter
@@ -5235,7 +5239,7 @@ function _acceptGovernedTurnLocked(root, config, opts) {
5235
5239
  || null,
5236
5240
  acceptance_contract: rawIdleResult?.new_intake_intent?.acceptance_contract
5237
5241
  || [],
5238
- suppressed_transition: turnResult.phase_transition_request,
5242
+ suppressed_transition: turnResult.phase_transition_request || 'implementation',
5239
5243
  source_turn_id: turnResult.turn_id,
5240
5244
  recorded_at: now,
5241
5245
  };
@@ -5245,9 +5249,10 @@ function _acceptGovernedTurnLocked(root, config, opts) {
5245
5249
  status: updatedState.status,
5246
5250
  turn: { turn_id: currentTurn.turn_id, role_id: currentTurn.assigned_role },
5247
5251
  payload: {
5248
- suppressed_transition: turnResult.phase_transition_request,
5252
+ suppressed_transition: turnResult.phase_transition_request || 'implementation',
5249
5253
  reason: 'New intake from idle expansion must be materialized into planning artifacts before implementation dispatch.',
5250
5254
  new_intake_charter: updatedState.charter_materialization_pending.charter,
5255
+ suppressed_needs_human: suppressIdleExpansionNeedsHuman,
5251
5256
  },
5252
5257
  });
5253
5258
  }
@@ -5259,7 +5264,7 @@ function _acceptGovernedTurnLocked(root, config, opts) {
5259
5264
  }
5260
5265
 
5261
5266
  const hasRemainingTurns = Object.keys(remainingTurns).length > 0;
5262
- if (turnResult.status !== 'needs_human') {
5267
+ if (turnResult.status !== 'needs_human' || suppressIdleExpansionNeedsHuman) {
5263
5268
  if (hasRemainingTurns) {
5264
5269
  if (turnResult.run_completion_request && !updatedState.queued_run_completion) {
5265
5270
  updatedState.queued_run_completion = {