@onepointfour-packs/flaredeck-agent 0.2.185 → 0.2.187

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/serve.js CHANGED
@@ -53,6 +53,7 @@ import { EventStream, describeFailure } from './stream.js';
53
53
  import { mergeableComments, combineComments, unparkReason, isBossInstruction, isTeamAsk } from './comment-merge.js';
54
54
  import { LogShipper } from './log-ship.js';
55
55
  import { isOwnMove } from './own-moves.js';
56
+ import { baseBranchesFromComponent } from './workspace.js';
56
57
  import { isAfterReleaseMove } from './flaredeck-proxy.js';
57
58
  import { canPublish, canPushTo } from './githubauth.js';
58
59
  /**
@@ -1037,7 +1038,10 @@ export async function serve(opts) {
1037
1038
  log('event', 'At this pane? Type /local and questions for you are shown here instead of on your phone (back to /remote after ten minutes unanswered).');
1038
1039
  log('event', 'Merges and promotes go through release, which follows the board\'s rules for who may release where. Deletes and other limits are instructions for now (docs/spec-manager-role.md §14).');
1039
1040
  }
1040
- if ((isLead || isManager) && opts.deck && !releaseSet) {
1041
+ // Workers get it too: the board's hotfix agent ships its own hotfixes (docs/spec-hotfix.md). The
1042
+ // tool itself refuses whatever the board does not allow a worker; it is mounted on a worker's turn
1043
+ // only for a hotfix card.
1044
+ if (opts.deck && !releaseSet) {
1041
1045
  const tenant = tenantFromToken(await opts.auth());
1042
1046
  const deck = opts.deck;
1043
1047
  if (tenant) {
@@ -1073,6 +1077,18 @@ export async function serve(opts) {
1073
1077
  c.on('error', () => res(''));
1074
1078
  }),
1075
1079
  log: (t) => log('tool', t),
1080
+ self: () => selfId ?? '',
1081
+ cardInfo: async (code) => {
1082
+ const c = await deck.card(code).catch(() => null);
1083
+ if (!c)
1084
+ return null;
1085
+ const since = c.release_since ? Date.parse(String(c.release_since)) : NaN;
1086
+ return {
1087
+ laneType: c.lane?.type ?? null,
1088
+ minutesInReleaseLane: String(c.lane?.type || '').toLowerCase() === 'release' && Number.isFinite(since) ? (Date.now() - since) / 60_000 : null,
1089
+ releases: (Array.isArray(c.releases) ? c.releases : []).map((r) => ({ environment: String(r.environment ?? ''), sha: String(r.sha ?? '') })),
1090
+ };
1091
+ },
1076
1092
  record: async (card, text) => { await callFlaredeckTool(config.mcpUrl, await opts.auth(), 'create-card-comment', { card_id: card, comment: text }); },
1077
1093
  recordReleased: async (o) => {
1078
1094
  const res = await fetch(`${new URL(config.mcpUrl).origin}/api/v1/${encodeURIComponent(tenant)}/cards/released`, {
@@ -2655,13 +2671,18 @@ export async function serve(opts) {
2655
2671
  /** The person this would go to may be at this pane: flag it, and keep it off their phone. */
2656
2672
  hold,
2657
2673
  /** It goes with a script request, which notifies by itself: list the card, ring nobody. */
2658
- quiet) => {
2674
+ quiet,
2675
+ /** A wall the harness found itself, not the model asking: not judged for "could do it yourself". */
2676
+ wall) => {
2659
2677
  const label = ref.code || ref.card || 'card';
2660
2678
  try {
2661
2679
  const out = await callFlaredeckTool(config.mcpUrl, await opts.auth(), 'set-needs-human', {
2662
2680
  // The code when there is one — it resolves on its own. The board always goes with the key.
2663
2681
  card_id: ref.code || ref.card,
2664
2682
  ...(ref.board ? { board_id: ref.board } : {}),
2683
+ // The harness found this wall itself; the model did not reach for help. Not judged for
2684
+ // "could you do it yourself" — C3PO's "which repository?" was refused that way, silently.
2685
+ ...(wall ? { harness_wall: true } : {}),
2665
2686
  reason,
2666
2687
  ...(kind ? { kind } : {}),
2667
2688
  ...(ask ? { question: ask.question, options: ask.options } : {}),
@@ -2701,7 +2722,7 @@ export async function serve(opts) {
2701
2722
  card: event.cardKey ?? '',
2702
2723
  ...(event.boardKey ? { board: event.boardKey } : {}),
2703
2724
  ...(event.cardCode ? { code: event.cardCode } : {}),
2704
- }, reason, kind);
2725
+ }, reason, kind, undefined, undefined, undefined, undefined, true);
2705
2726
  };
2706
2727
  async function handle(event) {
2707
2728
  const prefix = codePrefix(event.cardCode);
@@ -2831,6 +2852,13 @@ export async function serve(opts) {
2831
2852
  // it may work in — one repo, or every repo in that folder — and the card says which of them
2832
2853
  // this work belongs to. See workspace.ts: the folder is the only declaration, so there is no
2833
2854
  // config list to go stale.
2855
+ // No repository details on the event (the harness's own pickup): the card names its repository.
2856
+ if (event.cardKey && !(event.baseBranches ?? []).length && opts.deck) {
2857
+ const here = await opts.deck.card(event.cardCode || event.cardKey).catch(() => null);
2858
+ const fromCard = baseBranchesFromComponent(here?.component);
2859
+ if (fromCard.length)
2860
+ event = { ...event, baseBranches: fromCard };
2861
+ }
2834
2862
  const where = await resolveCheckout(config.repo, event, scope, opts.agent ?? '');
2835
2863
  let chosen = where.kind === 'repo' ? where.checkout : null;
2836
2864
  let fromCard = where.kind === 'repo' && where.branch ? { branch: where.branch, role: where.role } : null;
@@ -2871,7 +2899,8 @@ export async function serve(opts) {
2871
2899
  const willChange = !!event.cardKey && event.event !== 'review' && event.event !== 'intake' && event.event !== 'ask' && event.forYou !== false && !isLeadDispatch(event, isLead);
2872
2900
  if (!chosen && willChange && (where.kind === 'ambiguous' || where.kind === 'unknown')) {
2873
2901
  const reason = askingForComponent(where);
2874
- await flagForHuman(event, reason);
2902
+ // Which repository is a technical question about the board's setup, not a business one: COR-0311 (19 Oct) went to its reporter.
2903
+ await flagForHuman(event, reason, 'technical');
2875
2904
  return;
2876
2905
  }
2877
2906
  // A FIX with no branch of its own is the one case worth stopping for.
@@ -3696,7 +3725,7 @@ export async function serve(opts) {
3696
3725
  flaredeck,
3697
3726
  runs: runsSet,
3698
3727
  schedules: schedulesSet,
3699
- ...(releaseSet && (isLead || isManager) ? { release: releaseSet } : {}),
3728
+ ...(releaseSet && (isLead || isManager || event.hotfix === true) ? { release: releaseSet } : {}),
3700
3729
  },
3701
3730
  guard: {
3702
3731
  ...guard,