@nelsonlongxiang/dsh-open-a2a-net 0.6.36 → 0.6.38

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.
Files changed (51) hide show
  1. package/README.md +7 -4
  2. package/README_zh.md +7 -4
  3. package/assets/nexusDist/assets/index-C7HagaGy.css +1 -0
  4. package/assets/nexusDist/assets/index-NkNSeIx6.js +9 -0
  5. package/assets/nexusDist/index.html +2 -2
  6. package/cli/a2a.mjs +35 -5
  7. package/lib/a2a-client.js +3 -3
  8. package/lib/canvas-discovery.js +29 -0
  9. package/lib/client.js +63 -63
  10. package/lib/direct-forward.js +49 -27
  11. package/lib/faces/admin.js +30 -4
  12. package/lib/faces/canvas-notify.js +8 -6
  13. package/lib/faces/wire.js +29 -10
  14. package/lib/index.js +107 -24
  15. package/lib/origin-claim.js +31 -8
  16. package/lib/owner-settings.js +38 -1
  17. package/lib/peer-directory.js +1 -1
  18. package/lib/peer-link-display.js +15 -0
  19. package/lib/skill/references/cli.md +11 -3
  20. package/lib/skill/references/pitfalls.md +1 -1
  21. package/lib/skill/references/setup.md +9 -9
  22. package/lib/task-auth.js +1 -1
  23. package/lib/task-ledger-rows.js +220 -0
  24. package/lib/task-ledger.js +13 -196
  25. package/lib/tools/info.js +14 -5
  26. package/lib/types/a2a-client.d.ts +1 -0
  27. package/lib/types/canvas-discovery.d.ts +20 -0
  28. package/lib/types/direct-forward.d.ts +9 -0
  29. package/lib/types/faces/admin.d.ts +11 -0
  30. package/lib/types/faces/canvas-notify.d.ts +3 -1
  31. package/lib/types/faces/wire.d.ts +4 -1
  32. package/lib/types/index.d.ts +3 -1
  33. package/lib/types/origin-claim.d.ts +16 -4
  34. package/lib/types/owner-settings.d.ts +5 -0
  35. package/lib/types/peer-link-display.d.ts +13 -0
  36. package/lib/types/task-auth.d.ts +2 -0
  37. package/lib/types/task-ledger-rows.d.ts +352 -0
  38. package/lib/types/task-ledger.d.ts +7 -288
  39. package/lib/types/tools/info.d.ts +5 -0
  40. package/lib/types/types.d.ts +2 -1
  41. package/nexus-stage/src/planning-member-handles.ts +28 -1
  42. package/nexus-stage/src/planning-menus.ts +12 -5
  43. package/nexus-stage/src/planning-view.ts +18 -14
  44. package/nexus-stage/src/seat-name-editor.ts +31 -0
  45. package/nexus-stage/src/session-drag.ts +64 -0
  46. package/nexus-stage/src/wb-directory.ts +2 -0
  47. package/nexus-stage/src/workbench.css +2 -0
  48. package/nexus-stage/src/workbench.ts +16 -5
  49. package/package.json +1 -1
  50. package/assets/nexusDist/assets/index-DIQNDBy6.js +0 -9
  51. package/assets/nexusDist/assets/index-z33lHtLw.css +0 -1
package/lib/client.js CHANGED
@@ -22,8 +22,8 @@ window.__ModuleLoader__.load({
22
22
  return n;
23
23
  }
24
24
  //#endregion
25
- //#region \0dsh-css:D:\workspace\dsh-open-a2a-net\tmp\release-0.6.36-51b18cc\src\src\client\A2aControl.module.css.mjs
26
- const css = ".KfHpVG_wrap{max-width:100%;display:inline-flex;position:relative}.KfHpVG_trigger{align-items:center;gap:4px;display:inline-flex;position:relative}.KfHpVG_unreadBadge{text-align:center;color:#fff;background:var(--dsh-accent,#4c8bf5);border-radius:7px;min-width:14px;height:14px;padding:0 3px;font-size:9px;line-height:14px;animation:2s ease-in-out infinite KfHpVG_badgePulse;position:absolute;top:-6px;right:-8px}@keyframes KfHpVG_badgePulse{0%,to{box-shadow:0 0 #4c8bf573}50%{box-shadow:0 0 0 4px #4c8bf500}}@media (prefers-reduced-motion:reduce){.KfHpVG_unreadBadge{animation:none}}.KfHpVG_popover{z-index:1100;border:1px solid var(--dsh-border,#2a2a30);background:var(--dsh-surface-raised,#1c1c22);border-radius:10px;flex-direction:column;gap:8px;width:320px;max-width:calc(100vw - 16px);max-height:60vh;padding:12px;display:flex;position:fixed;overflow-y:auto;box-shadow:0 8px 24px #00000059}.KfHpVG_title{font-size:13px;font-weight:600}.KfHpVG_sectionTitle{border-top:1px solid var(--dsh-border,#2a2a30);text-transform:uppercase;letter-spacing:.04em;opacity:.65;margin-top:4px;padding-top:8px;font-size:11px;font-weight:600}.KfHpVG_stageRow{border-top:1px solid var(--dsh-border,#2a2a30);align-items:center;gap:8px;margin-top:4px;padding-top:8px;display:flex}.KfHpVG_stageTitle{text-transform:uppercase;letter-spacing:.04em;opacity:.65;font-size:11px;font-weight:600}.KfHpVG_stageLinks{gap:6px;display:flex}.KfHpVG_stageLink{border:1px solid var(--dsh-border,#2a2a30);color:inherit;border-radius:999px;align-items:center;padding:3px 8px;font-size:11px;text-decoration:none;display:inline-flex}.KfHpVG_stageLink:hover,.KfHpVG_stageLink:focus-visible{border-color:var(--dsh-accent,#4c8bf5)}.KfHpVG_empty{opacity:.7;font-size:12px}.KfHpVG_row{border:1px solid #0000;border-radius:8px;align-items:center;gap:10px;padding:8px;display:flex}.KfHpVG_joined{border-color:var(--dsh-border,#2a2a30)}.KfHpVG_facts{text-align:left;min-width:0;color:inherit;font:inherit;cursor:pointer;background:0 0;border:none;border-radius:6px;flex-direction:column;flex:1;gap:2px;padding:2px 4px;display:flex}.KfHpVG_facts:hover{background:var(--dsh-surface-hover,#ffffff0f)}.KfHpVG_nameRow{align-items:center;gap:6px;display:flex}.KfHpVG_stateDot{border-radius:50%;flex:none;width:7px;height:7px}.KfHpVG_stateDot.KfHpVG_live{background:var(--dsh-accent,#4c8bf5)}.KfHpVG_stateDot.KfHpVG_live.KfHpVG_joined{background:var(--dsh-success,#3fb96f)}.KfHpVG_stateDot.KfHpVG_cold{background:var(--dsh-warning,#d9a13c)}.KfHpVG_name{text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500;overflow:hidden}.KfHpVG_description{opacity:.7;text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.KfHpVG_team{opacity:.55;font-size:11px;font-family:var(--dsh-mono,monospace)}.KfHpVG_note{opacity:.6;font-size:11px}.KfHpVG_searchInput{box-sizing:border-box;border:1px solid var(--dsh-border,#2a2a30);background:var(--dsh-surface,#141419);width:100%;color:inherit;border-radius:6px;padding:5px 8px;font-size:12px}.KfHpVG_searchInput:focus{outline:1px solid var(--dsh-accent,#4c8bf5)}.KfHpVG_sessionGroup{flex-direction:column;gap:2px;display:flex}.KfHpVG_groupHead{color:inherit;font:inherit;opacity:.75;cursor:pointer;text-align:left;background:0 0;border:none;align-items:center;gap:6px;padding:4px 2px;font-size:11px;font-weight:600;display:flex}.KfHpVG_groupChevron{transition:transform .12s;display:inline-block}.KfHpVG_groupChevronClosed{transform:rotate(-90deg)}.KfHpVG_groupTag{text-overflow:ellipsis;white-space:nowrap;border:1px solid var(--dsh-border,#2a2a30);opacity:.8;border-radius:999px;flex:none;max-width:90px;padding:0 6px;font-size:10px;line-height:16px;overflow:hidden}.KfHpVG_groupButton{width:24px;height:24px;color:inherit;opacity:.55;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:6px;flex:none;font-size:12px;line-height:1}.KfHpVG_groupButton:hover{border-color:var(--dsh-border,#2a2a30);opacity:1}.KfHpVG_groupPicker{border:1px dashed var(--dsh-border,#2a2a30);border-radius:8px;flex-wrap:wrap;gap:6px;width:100%;padding:8px;display:flex;position:relative}.KfHpVG_groupOption{border:1px solid var(--dsh-border,#2a2a30);color:inherit;cursor:pointer;background:0 0;border-radius:999px;padding:2px 8px;font-size:11px}.KfHpVG_groupOptionActive{border-color:var(--dsh-accent,#4c8bf5);color:var(--dsh-accent,#4c8bf5)}.KfHpVG_groupNew{align-items:center;gap:6px;width:100%;margin-top:2px;display:flex}.KfHpVG_groupInput{border:1px solid var(--dsh-border,#2a2a30);background:var(--dsh-surface,#141419);min-width:0;color:inherit;border-radius:6px;flex:1;padding:4px 8px;font-size:12px}.KfHpVG_peerGrid{flex-wrap:wrap;gap:6px;display:flex}.KfHpVG_peerChip{border:1px solid var(--dsh-border,#2a2a30);border-radius:999px;align-items:center;gap:6px;padding:3px 8px;font-size:11px;display:inline-flex}.KfHpVG_peerHost{text-overflow:ellipsis;white-space:nowrap;max-width:160px;overflow:hidden}.KfHpVG_peerScore{font-family:var(--dsh-mono,monospace);opacity:.6}.KfHpVG_activityList{flex-direction:column;gap:2px;display:flex}.KfHpVG_activityRow{border-radius:4px;align-items:center;gap:8px;padding:2px 4px;font-size:11px;display:flex}.KfHpVG_activityRow[data-ok=false]{opacity:.55}.KfHpVG_activityDir{flex:none;font-weight:600}.KfHpVG_activityDir.KfHpVG_in{color:var(--dsh-success,#3fb96f)}.KfHpVG_activityDir.KfHpVG_out{color:var(--dsh-accent,#4c8bf5)}.KfHpVG_activityTeam{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-family:var(--dsh-mono,monospace);opacity:.8;flex:1;overflow:hidden}.KfHpVG_activityPeer{text-overflow:ellipsis;white-space:nowrap;opacity:.55;max-width:90px;overflow:hidden}.KfHpVG_activityTime{opacity:.5;font-family:var(--dsh-mono,monospace);flex:none}.KfHpVG_activityRow[data-jump=true]{cursor:pointer}.KfHpVG_activityRow[data-jump=true]:hover{background:var(--dsh-surface-hover,#ffffff0f)}.KfHpVG_fresh{animation:1.2s ease-out 3 KfHpVG_freshFlash}@keyframes KfHpVG_freshFlash{0%{background:#4c8bf538}to{background:0 0}}@media (prefers-reduced-motion:reduce){.KfHpVG_fresh{background:#4c8bf51f;animation:none}}.KfHpVG_inFlightList{flex-direction:column;gap:2px;margin-bottom:4px;display:flex}.KfHpVG_inFlightRow{border:1px dashed var(--dsh-border,#2a2a30);border-radius:4px;align-items:center;gap:8px;padding:3px 4px;font-size:11px;display:flex}.KfHpVG_inFlightPulse{background:var(--dsh-accent,#4c8bf5);border-radius:50%;flex:none;width:7px;height:7px;animation:1.1s ease-in-out infinite KfHpVG_flightPulse}@keyframes KfHpVG_flightPulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.55)}}@media (prefers-reduced-motion:reduce){.KfHpVG_inFlightPulse{animation:none}}.KfHpVG_versionTag{color:var(--dsh-color-text-tertiary,#888);background:var(--dsh-color-bg-secondary,#8080801f);border-radius:999px;margin-left:auto;padding:1px 6px;font-size:10px;font-weight:500}.KfHpVG_inFlightStale{opacity:.55}.KfHpVG_canvasCreate{align-items:center;gap:6px;margin-bottom:4px;display:flex}.KfHpVG_canvasCreate .KfHpVG_groupInput{flex:1;min-width:0}.KfHpVG_canvasChip{max-width:100%;color:var(--dsh-color-text-secondary,#aaa);background:var(--dsh-color-bg-secondary,#8080801f);border-radius:999px;align-items:center;gap:4px;margin:2px 4px 2px 0;padding:1px 6px;font-size:11px;display:inline-flex}.KfHpVG_canvasChip>span:not([class]){white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}.KfHpVG_canvasChipRemove{cursor:pointer;min-width:24px;min-height:24px;color:var(--dsh-color-text-tertiary,#888);background:0 0;border:none;padding:2px 5px;font-size:11px;line-height:1}.KfHpVG_canvasChipRemove:hover{color:var(--dsh-color-text-primary,#eee)}.KfHpVG_canvasChipRemove{transition:color .14s ease-out}@media (prefers-reduced-motion:reduce){.KfHpVG_canvasChipRemove{transition:none}}.KfHpVG_canvasChipRemove:focus-visible{outline:2px solid var(--dsh-color-primary,#4f8cff);outline-offset:1px;border-radius:4px}.KfHpVG_canvasActions{gap:6px;margin-top:4px;display:flex}.KfHpVG_inFlightStale .KfHpVG_inFlightPulse{opacity:.4;animation:none}.KfHpVG_failMark{color:#f87171;flex-shrink:0;font-size:11px}.KfHpVG_staleNote{color:var(--dsh-text-muted,#94a3b8b3);padding:4px 8px;font-size:11px}";
25
+ //#region \0dsh-css:D:\workspace\dsh-open-a2a-net\tmp\release-0.6.38-35f5893\src\src\client\A2aControl.module.css.mjs
26
+ const css = ".pFp7aa_wrap{max-width:100%;display:inline-flex;position:relative}.pFp7aa_trigger{align-items:center;gap:4px;display:inline-flex;position:relative}.pFp7aa_unreadBadge{text-align:center;color:#fff;background:var(--dsh-accent,#4c8bf5);border-radius:7px;min-width:14px;height:14px;padding:0 3px;font-size:9px;line-height:14px;animation:2s ease-in-out infinite pFp7aa_badgePulse;position:absolute;top:-6px;right:-8px}@keyframes pFp7aa_badgePulse{0%,to{box-shadow:0 0 #4c8bf573}50%{box-shadow:0 0 0 4px #4c8bf500}}@media (prefers-reduced-motion:reduce){.pFp7aa_unreadBadge{animation:none}}.pFp7aa_popover{z-index:1100;border:1px solid var(--dsh-border,#2a2a30);background:var(--dsh-surface-raised,#1c1c22);border-radius:10px;flex-direction:column;gap:8px;width:320px;max-width:calc(100vw - 16px);max-height:60vh;padding:12px;display:flex;position:fixed;overflow-y:auto;box-shadow:0 8px 24px #00000059}.pFp7aa_title{font-size:13px;font-weight:600}.pFp7aa_sectionTitle{border-top:1px solid var(--dsh-border,#2a2a30);text-transform:uppercase;letter-spacing:.04em;opacity:.65;margin-top:4px;padding-top:8px;font-size:11px;font-weight:600}.pFp7aa_stageRow{border-top:1px solid var(--dsh-border,#2a2a30);align-items:center;gap:8px;margin-top:4px;padding-top:8px;display:flex}.pFp7aa_stageTitle{text-transform:uppercase;letter-spacing:.04em;opacity:.65;font-size:11px;font-weight:600}.pFp7aa_stageLinks{gap:6px;display:flex}.pFp7aa_stageLink{border:1px solid var(--dsh-border,#2a2a30);color:inherit;border-radius:999px;align-items:center;padding:3px 8px;font-size:11px;text-decoration:none;display:inline-flex}.pFp7aa_stageLink:hover,.pFp7aa_stageLink:focus-visible{border-color:var(--dsh-accent,#4c8bf5)}.pFp7aa_empty{opacity:.7;font-size:12px}.pFp7aa_row{border:1px solid #0000;border-radius:8px;align-items:center;gap:10px;padding:8px;display:flex}.pFp7aa_joined{border-color:var(--dsh-border,#2a2a30)}.pFp7aa_facts{text-align:left;min-width:0;color:inherit;font:inherit;cursor:pointer;background:0 0;border:none;border-radius:6px;flex-direction:column;flex:1;gap:2px;padding:2px 4px;display:flex}.pFp7aa_facts:hover{background:var(--dsh-surface-hover,#ffffff0f)}.pFp7aa_nameRow{align-items:center;gap:6px;display:flex}.pFp7aa_stateDot{border-radius:50%;flex:none;width:7px;height:7px}.pFp7aa_stateDot.pFp7aa_live{background:var(--dsh-accent,#4c8bf5)}.pFp7aa_stateDot.pFp7aa_live.pFp7aa_joined{background:var(--dsh-success,#3fb96f)}.pFp7aa_stateDot.pFp7aa_cold{background:var(--dsh-warning,#d9a13c)}.pFp7aa_name{text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500;overflow:hidden}.pFp7aa_description{opacity:.7;text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.pFp7aa_team{opacity:.55;font-size:11px;font-family:var(--dsh-mono,monospace)}.pFp7aa_note{opacity:.6;font-size:11px}.pFp7aa_searchInput{box-sizing:border-box;border:1px solid var(--dsh-border,#2a2a30);background:var(--dsh-surface,#141419);width:100%;color:inherit;border-radius:6px;padding:5px 8px;font-size:12px}.pFp7aa_searchInput:focus{outline:1px solid var(--dsh-accent,#4c8bf5)}.pFp7aa_sessionGroup{flex-direction:column;gap:2px;display:flex}.pFp7aa_groupHead{color:inherit;font:inherit;opacity:.75;cursor:pointer;text-align:left;background:0 0;border:none;align-items:center;gap:6px;padding:4px 2px;font-size:11px;font-weight:600;display:flex}.pFp7aa_groupChevron{transition:transform .12s;display:inline-block}.pFp7aa_groupChevronClosed{transform:rotate(-90deg)}.pFp7aa_groupTag{text-overflow:ellipsis;white-space:nowrap;border:1px solid var(--dsh-border,#2a2a30);opacity:.8;border-radius:999px;flex:none;max-width:90px;padding:0 6px;font-size:10px;line-height:16px;overflow:hidden}.pFp7aa_groupButton{width:24px;height:24px;color:inherit;opacity:.55;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:6px;flex:none;font-size:12px;line-height:1}.pFp7aa_groupButton:hover{border-color:var(--dsh-border,#2a2a30);opacity:1}.pFp7aa_groupPicker{border:1px dashed var(--dsh-border,#2a2a30);border-radius:8px;flex-wrap:wrap;gap:6px;width:100%;padding:8px;display:flex;position:relative}.pFp7aa_groupOption{border:1px solid var(--dsh-border,#2a2a30);color:inherit;cursor:pointer;background:0 0;border-radius:999px;padding:2px 8px;font-size:11px}.pFp7aa_groupOptionActive{border-color:var(--dsh-accent,#4c8bf5);color:var(--dsh-accent,#4c8bf5)}.pFp7aa_groupNew{align-items:center;gap:6px;width:100%;margin-top:2px;display:flex}.pFp7aa_groupInput{border:1px solid var(--dsh-border,#2a2a30);background:var(--dsh-surface,#141419);min-width:0;color:inherit;border-radius:6px;flex:1;padding:4px 8px;font-size:12px}.pFp7aa_peerGrid{flex-wrap:wrap;gap:6px;display:flex}.pFp7aa_peerChip{border:1px solid var(--dsh-border,#2a2a30);border-radius:999px;align-items:center;gap:6px;padding:3px 8px;font-size:11px;display:inline-flex}.pFp7aa_peerHost{text-overflow:ellipsis;white-space:nowrap;max-width:160px;overflow:hidden}.pFp7aa_peerScore{font-family:var(--dsh-mono,monospace);opacity:.6}.pFp7aa_activityList{flex-direction:column;gap:2px;display:flex}.pFp7aa_activityRow{border-radius:4px;align-items:center;gap:8px;padding:2px 4px;font-size:11px;display:flex}.pFp7aa_activityRow[data-ok=false]{opacity:.55}.pFp7aa_activityDir{flex:none;font-weight:600}.pFp7aa_activityDir.pFp7aa_in{color:var(--dsh-success,#3fb96f)}.pFp7aa_activityDir.pFp7aa_out{color:var(--dsh-accent,#4c8bf5)}.pFp7aa_activityTeam{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-family:var(--dsh-mono,monospace);opacity:.8;flex:1;overflow:hidden}.pFp7aa_activityPeer{text-overflow:ellipsis;white-space:nowrap;opacity:.55;max-width:90px;overflow:hidden}.pFp7aa_activityTime{opacity:.5;font-family:var(--dsh-mono,monospace);flex:none}.pFp7aa_activityRow[data-jump=true]{cursor:pointer}.pFp7aa_activityRow[data-jump=true]:hover{background:var(--dsh-surface-hover,#ffffff0f)}.pFp7aa_fresh{animation:1.2s ease-out 3 pFp7aa_freshFlash}@keyframes pFp7aa_freshFlash{0%{background:#4c8bf538}to{background:0 0}}@media (prefers-reduced-motion:reduce){.pFp7aa_fresh{background:#4c8bf51f;animation:none}}.pFp7aa_inFlightList{flex-direction:column;gap:2px;margin-bottom:4px;display:flex}.pFp7aa_inFlightRow{border:1px dashed var(--dsh-border,#2a2a30);border-radius:4px;align-items:center;gap:8px;padding:3px 4px;font-size:11px;display:flex}.pFp7aa_inFlightPulse{background:var(--dsh-accent,#4c8bf5);border-radius:50%;flex:none;width:7px;height:7px;animation:1.1s ease-in-out infinite pFp7aa_flightPulse}@keyframes pFp7aa_flightPulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.55)}}@media (prefers-reduced-motion:reduce){.pFp7aa_inFlightPulse{animation:none}}.pFp7aa_versionTag{color:var(--dsh-color-text-tertiary,#888);background:var(--dsh-color-bg-secondary,#8080801f);border-radius:999px;margin-left:auto;padding:1px 6px;font-size:10px;font-weight:500}.pFp7aa_inFlightStale{opacity:.55}.pFp7aa_canvasCreate{align-items:center;gap:6px;margin-bottom:4px;display:flex}.pFp7aa_canvasCreate .pFp7aa_groupInput{flex:1;min-width:0}.pFp7aa_canvasChip{max-width:100%;color:var(--dsh-color-text-secondary,#aaa);background:var(--dsh-color-bg-secondary,#8080801f);border-radius:999px;align-items:center;gap:4px;margin:2px 4px 2px 0;padding:1px 6px;font-size:11px;display:inline-flex}.pFp7aa_canvasChip>span:not([class]){white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}.pFp7aa_canvasChipRemove{cursor:pointer;min-width:24px;min-height:24px;color:var(--dsh-color-text-tertiary,#888);background:0 0;border:none;padding:2px 5px;font-size:11px;line-height:1}.pFp7aa_canvasChipRemove:hover{color:var(--dsh-color-text-primary,#eee)}.pFp7aa_canvasChipRemove{transition:color .14s ease-out}@media (prefers-reduced-motion:reduce){.pFp7aa_canvasChipRemove{transition:none}}.pFp7aa_canvasChipRemove:focus-visible{outline:2px solid var(--dsh-color-primary,#4f8cff);outline-offset:1px;border-radius:4px}.pFp7aa_canvasActions{gap:6px;margin-top:4px;display:flex}.pFp7aa_inFlightStale .pFp7aa_inFlightPulse{opacity:.4;animation:none}.pFp7aa_failMark{color:#f87171;flex-shrink:0;font-size:11px}.pFp7aa_staleNote{color:var(--dsh-text-muted,#94a3b8b3);padding:4px 8px;font-size:11px}";
27
27
  const tagId = "@nelsonlongxiang/dsh-open-a2a-net/A2aControl.module.css";
28
28
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
29
29
  const tag = document.createElement("style");
@@ -33,67 +33,67 @@ window.__ModuleLoader__.load({
33
33
  document.head.appendChild(tag);
34
34
  }
35
35
  var A2aControl_module_css_default = {
36
- "activityDir": "KfHpVG_activityDir",
37
- "activityList": "KfHpVG_activityList",
38
- "activityPeer": "KfHpVG_activityPeer",
39
- "activityRow": "KfHpVG_activityRow",
40
- "activityTeam": "KfHpVG_activityTeam",
41
- "activityTime": "KfHpVG_activityTime",
42
- "badgePulse": "KfHpVG_badgePulse",
43
- "canvasActions": "KfHpVG_canvasActions",
44
- "canvasChip": "KfHpVG_canvasChip",
45
- "canvasChipRemove": "KfHpVG_canvasChipRemove",
46
- "canvasCreate": "KfHpVG_canvasCreate",
47
- "cold": "KfHpVG_cold",
48
- "description": "KfHpVG_description",
49
- "empty": "KfHpVG_empty",
50
- "facts": "KfHpVG_facts",
51
- "failMark": "KfHpVG_failMark",
52
- "flightPulse": "KfHpVG_flightPulse",
53
- "fresh": "KfHpVG_fresh",
54
- "freshFlash": "KfHpVG_freshFlash",
55
- "groupButton": "KfHpVG_groupButton",
56
- "groupChevron": "KfHpVG_groupChevron",
57
- "groupChevronClosed": "KfHpVG_groupChevronClosed",
58
- "groupHead": "KfHpVG_groupHead",
59
- "groupInput": "KfHpVG_groupInput",
60
- "groupNew": "KfHpVG_groupNew",
61
- "groupOption": "KfHpVG_groupOption",
62
- "groupOptionActive": "KfHpVG_groupOptionActive",
63
- "groupPicker": "KfHpVG_groupPicker",
64
- "groupTag": "KfHpVG_groupTag",
65
- "in": "KfHpVG_in",
66
- "inFlightList": "KfHpVG_inFlightList",
67
- "inFlightPulse": "KfHpVG_inFlightPulse",
68
- "inFlightRow": "KfHpVG_inFlightRow",
69
- "inFlightStale": "KfHpVG_inFlightStale",
70
- "joined": "KfHpVG_joined",
71
- "live": "KfHpVG_live",
72
- "name": "KfHpVG_name",
73
- "nameRow": "KfHpVG_nameRow",
74
- "note": "KfHpVG_note",
75
- "out": "KfHpVG_out",
76
- "peerChip": "KfHpVG_peerChip",
77
- "peerGrid": "KfHpVG_peerGrid",
78
- "peerHost": "KfHpVG_peerHost",
79
- "peerScore": "KfHpVG_peerScore",
80
- "popover": "KfHpVG_popover",
81
- "row": "KfHpVG_row",
82
- "searchInput": "KfHpVG_searchInput",
83
- "sectionTitle": "KfHpVG_sectionTitle",
84
- "sessionGroup": "KfHpVG_sessionGroup",
85
- "stageLink": "KfHpVG_stageLink",
86
- "stageLinks": "KfHpVG_stageLinks",
87
- "stageRow": "KfHpVG_stageRow",
88
- "stageTitle": "KfHpVG_stageTitle",
89
- "staleNote": "KfHpVG_staleNote",
90
- "stateDot": "KfHpVG_stateDot",
91
- "team": "KfHpVG_team",
92
- "title": "KfHpVG_title",
93
- "trigger": "KfHpVG_trigger",
94
- "unreadBadge": "KfHpVG_unreadBadge",
95
- "versionTag": "KfHpVG_versionTag",
96
- "wrap": "KfHpVG_wrap"
36
+ "activityDir": "pFp7aa_activityDir",
37
+ "activityList": "pFp7aa_activityList",
38
+ "activityPeer": "pFp7aa_activityPeer",
39
+ "activityRow": "pFp7aa_activityRow",
40
+ "activityTeam": "pFp7aa_activityTeam",
41
+ "activityTime": "pFp7aa_activityTime",
42
+ "badgePulse": "pFp7aa_badgePulse",
43
+ "canvasActions": "pFp7aa_canvasActions",
44
+ "canvasChip": "pFp7aa_canvasChip",
45
+ "canvasChipRemove": "pFp7aa_canvasChipRemove",
46
+ "canvasCreate": "pFp7aa_canvasCreate",
47
+ "cold": "pFp7aa_cold",
48
+ "description": "pFp7aa_description",
49
+ "empty": "pFp7aa_empty",
50
+ "facts": "pFp7aa_facts",
51
+ "failMark": "pFp7aa_failMark",
52
+ "flightPulse": "pFp7aa_flightPulse",
53
+ "fresh": "pFp7aa_fresh",
54
+ "freshFlash": "pFp7aa_freshFlash",
55
+ "groupButton": "pFp7aa_groupButton",
56
+ "groupChevron": "pFp7aa_groupChevron",
57
+ "groupChevronClosed": "pFp7aa_groupChevronClosed",
58
+ "groupHead": "pFp7aa_groupHead",
59
+ "groupInput": "pFp7aa_groupInput",
60
+ "groupNew": "pFp7aa_groupNew",
61
+ "groupOption": "pFp7aa_groupOption",
62
+ "groupOptionActive": "pFp7aa_groupOptionActive",
63
+ "groupPicker": "pFp7aa_groupPicker",
64
+ "groupTag": "pFp7aa_groupTag",
65
+ "in": "pFp7aa_in",
66
+ "inFlightList": "pFp7aa_inFlightList",
67
+ "inFlightPulse": "pFp7aa_inFlightPulse",
68
+ "inFlightRow": "pFp7aa_inFlightRow",
69
+ "inFlightStale": "pFp7aa_inFlightStale",
70
+ "joined": "pFp7aa_joined",
71
+ "live": "pFp7aa_live",
72
+ "name": "pFp7aa_name",
73
+ "nameRow": "pFp7aa_nameRow",
74
+ "note": "pFp7aa_note",
75
+ "out": "pFp7aa_out",
76
+ "peerChip": "pFp7aa_peerChip",
77
+ "peerGrid": "pFp7aa_peerGrid",
78
+ "peerHost": "pFp7aa_peerHost",
79
+ "peerScore": "pFp7aa_peerScore",
80
+ "popover": "pFp7aa_popover",
81
+ "row": "pFp7aa_row",
82
+ "searchInput": "pFp7aa_searchInput",
83
+ "sectionTitle": "pFp7aa_sectionTitle",
84
+ "sessionGroup": "pFp7aa_sessionGroup",
85
+ "stageLink": "pFp7aa_stageLink",
86
+ "stageLinks": "pFp7aa_stageLinks",
87
+ "stageRow": "pFp7aa_stageRow",
88
+ "stageTitle": "pFp7aa_stageTitle",
89
+ "staleNote": "pFp7aa_staleNote",
90
+ "stateDot": "pFp7aa_stateDot",
91
+ "team": "pFp7aa_team",
92
+ "title": "pFp7aa_title",
93
+ "trigger": "pFp7aa_trigger",
94
+ "unreadBadge": "pFp7aa_unreadBadge",
95
+ "versionTag": "pFp7aa_versionTag",
96
+ "wrap": "pFp7aa_wrap"
97
97
  };
98
98
  //#endregion
99
99
  //#region lib/client/A2aControl.js
@@ -2,6 +2,18 @@
2
2
  import { DEFAULT_HOP_BUDGET, guardHeader, mintGuard } from "./recursion-guard.js";
3
3
  import { briefOf } from "./task-ledger.js";
4
4
  import { parseReceipt } from "./receipt.js";
5
+ /** Birth barrier exists only for eligible, actively dispatched tasks; receipts never reserve a slot. */
6
+ export class ForwardBirths {
7
+ pending = new Map();
8
+ wait(taskId) { return this.pending.get(taskId); }
9
+ begin(taskId) {
10
+ if (this.pending.size >= 64 || this.pending.has(taskId))
11
+ return undefined;
12
+ let release;
13
+ this.pending.set(taskId, new Promise(resolve => { release = resolve; }));
14
+ return () => { this.pending.delete(taskId); release(); };
15
+ }
16
+ }
5
17
  export function createDirectForward(d) {
6
18
  return async (request) => {
7
19
  const refuse = (why) => ({ ok: false, code: -32000, task_id: request.taskId, error: 'not forwarded: ' + why });
@@ -22,39 +34,49 @@ export function createDirectForward(d) {
22
34
  for (const url of directory.urls) {
23
35
  if (!d.deposited(request.caller))
24
36
  return refuse('local CLI deposit expired before dispatch');
37
+ const peerCard = await directory.card(url);
38
+ const receivers = request.team.includes('/canvas/') ? peerCard?.canvasOrg?.find(row => row.team === request.team)?.members.filter(member => member !== request.caller) : undefined;
25
39
  if (request.team.includes('/canvas/')) {
26
- const card = await directory.card(url);
27
- const org = card?.canvasOrg?.find(row => row.team === request.team);
28
- const receivers = org?.members.filter(member => member !== request.caller) ?? [];
29
- if (receivers.length === 0 || receivers.some(member => !d.memberLinked(member))) {
40
+ if (receivers === undefined || receivers.length === 0 || receivers.some(member => !d.memberLinked(member))) {
30
41
  failures.push(url + ': forwarding a canvas requires a direct operator link to every receiving member for its receipt return path');
31
42
  continue;
32
43
  }
33
44
  }
34
- const answer = await d.dispatch(url, request, guard);
35
- const result = !answer.ok && answer.ownBudgetExhausted === true ? { ...answer, code: -32005 } : answer;
36
- d.activity(request.team, url, result.ok);
37
- if (result.ok || result.code === -32005) {
38
- // A receipt owes no receipt; native bridge rounds have no A2A receipt contract.
39
- // Match routeTrunk/trackOwedTask rather than manufacturing an unpayable debt.
40
- if (parseReceipt(request.message) !== null)
41
- return { ...result, receipt: 'none' };
42
- if (result.ok && result.bridge !== undefined)
43
- return { ...result, receipt: 'none' };
44
- const card = await directory.card(url);
45
- const birth = { forwarded: true, ...(card?.publicKey ? { peerKey: card.publicKey } : {}), ...(card?.session ? { peerHost: card.session } : {}), deliveredTo: result.ok ? result.deliveredTo ?? request.team : request.team };
46
- // Exactly one outbound row. A completed sync response is receiptless bookkeeping.
47
- if (result.ok && (result.task_status === 'TASK_STATE_COMPLETED' || result.receipt === 'none'))
48
- d.ledger.trackReceiptless(request.taskId, request.team, url, request.contextId, briefOf(request.message), { kind: 'cli', id: request.caller }, birth);
49
- else
50
- d.ledger.track(request.taskId, request.team, url, request.contextId, briefOf(request.message), { kind: 'cli', id: request.caller }, birth);
51
- return result;
45
+ // Reserve only after all eligibility/receiver-link gates. A receipt owes no receipt.
46
+ const receipt = parseReceipt(request.message) !== null;
47
+ const release = receipt ? () => { } : d.births.begin(request.taskId);
48
+ if (release === undefined)
49
+ return refuse('forward receipt correlation capacity is full');
50
+ try {
51
+ // The same selected-peer snapshot supplies both return-link gate and settlement birth.
52
+ const answer = await d.dispatch(url, request, guard);
53
+ const result = !answer.ok && answer.ownBudgetExhausted === true ? { ...answer, code: -32005 } : answer;
54
+ d.activity(request.team, url, result.ok);
55
+ if (result.ok || result.code === -32005) {
56
+ // A receipt owes no receipt; native bridge rounds have no A2A receipt contract.
57
+ // Match routeTrunk/trackOwedTask rather than manufacturing an unpayable debt.
58
+ if (parseReceipt(request.message) !== null)
59
+ return { ...result, receipt: 'none' };
60
+ if (result.ok && result.bridge !== undefined)
61
+ return { ...result, receipt: 'none' };
62
+ const deliveredTo = result.ok ? result.deliveredTo : undefined;
63
+ const birth = { forwarded: true, ...(request.auth !== undefined ? { auth: request.auth } : {}), ...(peerCard?.publicKey ? { peerKey: peerCard.publicKey } : {}), ...(peerCard?.session ? { peerHost: peerCard.session } : {}), ...(deliveredTo !== undefined ? { deliveredTo } : {}), ...(receivers !== undefined ? { receiverMembers: receivers } : {}) };
64
+ // Exactly one outbound row. A completed sync response is receiptless bookkeeping.
65
+ if (result.ok && (result.task_status === 'TASK_STATE_COMPLETED' || result.receipt === 'none'))
66
+ d.ledger.trackReceiptless(request.taskId, request.team, url, request.contextId, briefOf(request.message), { kind: 'cli', id: request.caller }, birth);
67
+ else
68
+ d.ledger.track(request.taskId, request.team, url, request.contextId, briefOf(request.message), { kind: 'cli', id: request.caller }, birth);
69
+ return result;
70
+ }
71
+ // The shared dispatcher's sync recovery query may prove a definitive failed
72
+ // outcome; like routeTrunk, that is an eligible miss, not an uncertain -32005.
73
+ failures.push(url + ': ' + result.error);
74
+ if (result.code === -32002 || result.code === -32003)
75
+ return { ...result, task_id: request.taskId };
76
+ }
77
+ finally {
78
+ release();
52
79
  }
53
- // The shared dispatcher's sync recovery query may prove a definitive failed
54
- // outcome; like routeTrunk, that is an eligible miss, not an uncertain -32005.
55
- failures.push(url + ': ' + result.error);
56
- if (result.code === -32002 || result.code === -32003)
57
- return { ...result, task_id: request.taskId };
58
80
  }
59
81
  return { ok: false, code: -32004, task_id: request.taskId, error: 'forwarding to "' + request.team + '" failed: ' + (failures.join('; ') || 'no eligible peer candidate') };
60
82
  };
@@ -21,7 +21,7 @@ import { fileURLToPath } from 'node:url';
21
21
  import { canvasNameReserved as reservedCanvasName, isRemoteMemberRef, validRemoteMemberRef } from "../canvas-store.js";
22
22
  import { refreshPeerCard, registerCanvasNotify } from "./canvas-notify.js";
23
23
  import { CLI_HANDLE_RE } from "../cli-deposit.js";
24
- import { applyOwnerSettings } from "../owner-settings.js";
24
+ import { applyOwnerSettings, parseSeatNames } from "../owner-settings.js";
25
25
  import { rosterOp } from "../roster-op.js";
26
26
  import { resolveStageMount } from "../stage-mount.js";
27
27
  import { ARCHIVE_CAP, archivedEndedBy } from "../task-ledger.js";
@@ -112,6 +112,7 @@ export function registerAdminFaces(webServer, d) {
112
112
  sessions.push({
113
113
  id,
114
114
  label: `${session}-${id8(id)}`,
115
+ ...(d.ownerSeatNames[id] !== undefined ? { name: d.ownerSeatNames[id], seatName: d.ownerSeatNames[id] } : {}),
115
116
  team: `${config.team}/${id8(id)}`,
116
117
  joined: true,
117
118
  live: false,
@@ -124,6 +125,7 @@ export function registerAdminFaces(webServer, d) {
124
125
  const body = JSON.stringify({
125
126
  nodes: true,
126
127
  zone: config.team,
128
+ ownerHost: d.ownCardUrl,
127
129
  version: PLUGIN_VERSION,
128
130
  // gh#193: a dark host says so — an empty remote list is not an outage.
129
131
  ...(config.isolated ? { isolated: true } : {}),
@@ -203,6 +205,9 @@ export function registerAdminFaces(webServer, d) {
203
205
  taskId: task.taskId, team: task.team, peer: task.peer, startedAt: task.startedAt, status: task.status,
204
206
  direction: task.direction ?? 'outbound', receiptExpected: task.receiptExpected !== false,
205
207
  ...(task.answeredAt !== undefined ? { answeredAt: task.answeredAt } : {}),
208
+ ...(task.forwarded === true ? { forwarded: true } : {}),
209
+ ...(task.deliveredTo !== undefined ? { deliveredTo: task.deliveredTo } : {}),
210
+ ...(task.receiverMembers !== undefined ? { receiverMembers: task.receiverMembers } : {}),
206
211
  // The dispatch brief rides every row: a task id and a team
207
212
  // say nothing about WHAT is owed — supervision cannot tell
208
213
  // a nudge from an abandon without it.
@@ -255,6 +260,8 @@ export function registerAdminFaces(webServer, d) {
255
260
  ...(row.summary !== undefined && row.summary !== '' ? { summary: row.summary.slice(0, 160) } : {}),
256
261
  ...(row.sender !== undefined ? { sender: row.sender } : {}),
257
262
  ...(row.forwarded === true ? { forwarded: true } : {}),
263
+ ...(row.deliveredTo !== undefined ? { deliveredTo: row.deliveredTo } : {}),
264
+ ...(row.receiverMembers !== undefined ? { receiverMembers: row.receiverMembers } : {}),
258
265
  ...(row.auth !== undefined ? { auth: row.auth } : {}),
259
266
  // #114 S4: who settled it and how they stood on the chain.
260
267
  ...(row.settledBy !== undefined ? { settledBy: row.settledBy } : {}),
@@ -269,6 +276,8 @@ export function registerAdminFaces(webServer, d) {
269
276
  const roles = archive.flatMap(row => (row.settledBy !== undefined ? [row.settledBy.chain_role] : []));
270
277
  return {
271
278
  mode: config.settlementAuth,
279
+ ...(d.enforceRefused !== undefined ? { enforceRefused: d.enforceRefused } : {}),
280
+ enforceBlockers: taskLedger.settlementEnforceBlockers(),
272
281
  offChain: roles.filter(r => r === 'off-chain').length,
273
282
  legacy: roles.filter(r => r === 'legacy').length,
274
283
  compat: roles.filter(r => r === 'compat').length,
@@ -326,6 +335,8 @@ export function registerAdminFaces(webServer, d) {
326
335
  // Of those, envelopes whose bound form missed here: the sender
327
336
  // dials an origin missing from originClaimAudiences.
328
337
  boundMissInbound: d.boundMissInboundCount,
338
+ // Who missed, by verified caller and the origin it dialled (newest first, ≤16).
339
+ boundMissRecent: d.boundMissRecent,
329
340
  // gh#113: CLI mailbox loss, all handles — mail the 64-row cap
330
341
  // pushed out, and mail cut at 4000 chars. The team verifier
331
342
  // holds `dropped` to zero.
@@ -522,12 +533,13 @@ export function registerAdminFaces(webServer, d) {
522
533
  }
523
534
  try {
524
535
  const target = peerUrl.replace(/\/$/, '') + '/a2a/canvas-op';
536
+ const body = JSON.stringify({ op: opV, name: nameV, ...(opV === 'add-member' || opV === 'remove-member' ? { id: idV !== '' && !isRemoteMemberRef(idV) && (sessionNodes.has(idV) || joinedSessions.has(idV)) ? `${config.team}/${id8(idV)}` : idV } : {}) });
525
537
  const reply = await d.peerFetch(target, {
526
538
  method: 'POST',
527
- headers: { 'Content-Type': 'application/json', ...originClaimHeaders({ url: target, method: 'POST' }) },
539
+ headers: { 'Content-Type': 'application/json', ...originClaimHeaders({ url: target, method: 'POST', body }) },
528
540
  // A local session id means nothing on the peer: send its zone/id8 handle (the workbench
529
541
  // drops a local card into a remote frame with its raw session id, which the peer refused).
530
- body: JSON.stringify({ op: opV, name: nameV, ...(opV === 'add-member' || opV === 'remove-member' ? { id: idV !== '' && !isRemoteMemberRef(idV) && (sessionNodes.has(idV) || joinedSessions.has(idV)) ? `${config.team}/${id8(idV)}` : idV } : {}) }),
542
+ body,
531
543
  signal: AbortSignal.timeout(15_000),
532
544
  });
533
545
  const raw = await reply.text();
@@ -602,6 +614,7 @@ export function registerAdminFaces(webServer, d) {
602
614
  if (req.method === 'GET') {
603
615
  const payload = JSON.stringify({
604
616
  ok: true,
617
+ seatNames: { ...d.ownerSeatNames },
605
618
  teamJoinAllowlist: [...d.ownerAllowlist],
606
619
  teamScopeInfo: d.ownerTeamScopeInfo,
607
620
  announce: config.announce === true,
@@ -614,6 +627,17 @@ export function registerAdminFaces(webServer, d) {
614
627
  readJsonBody(req, res, (rawBody) => {
615
628
  const body = rawBody;
616
629
  const changes = {};
630
+ if (Object.hasOwn(body, 'seatName') || Object.hasOwn(body, 'seatNames')) {
631
+ const names = body.seatNames;
632
+ const parsedNames = parseSeatNames(names);
633
+ if (Object.hasOwn(body, 'seatName') || parsedNames === undefined) {
634
+ const payload = JSON.stringify({ ok: false, error: 'seatNames must map full local session ids to nonempty display names (max 64 characters, no controls); clear by omitting the id' });
635
+ res.writeHead(400, { 'Content-Type': 'application/json' });
636
+ res.end(payload);
637
+ return;
638
+ }
639
+ changes.seatNames = parsedNames;
640
+ }
617
641
  if (Array.isArray(body.teamJoinAllowlist)) {
618
642
  const list = body.teamJoinAllowlist.filter((name) => typeof name === 'string' && name !== '' && name.length <= 128);
619
643
  changes.teamJoinAllowlist = [...new Set(list)];
@@ -634,7 +658,9 @@ export function registerAdminFaces(webServer, d) {
634
658
  const { yaml, restartNeeded } = applyOwnerSettings(current, changes);
635
659
  mkdirSync(home, { recursive: true });
636
660
  writeFileSync(patchPath, yaml, 'utf8');
637
- // Hot-apply the two call-time keys.
661
+ // Hot-apply the call-time owner keys.
662
+ if (changes.seatNames !== undefined)
663
+ d.ownerSeatNames = changes.seatNames;
638
664
  if (changes.teamJoinAllowlist !== undefined)
639
665
  d.ownerAllowlist = [...changes.teamJoinAllowlist];
640
666
  if (changes.teamScopeInfo !== undefined)
@@ -50,7 +50,7 @@ export function registerCanvasNotify(webServer, d) {
50
50
  const target = url.replace(/\/$/, '') + '/a2a/canvas-changed';
51
51
  void d.peerFetch(target, {
52
52
  method: 'POST',
53
- headers: { 'Content-Type': 'application/json', ...originClaimHeaders({ url: target, method: 'POST' }) },
53
+ headers: { 'Content-Type': 'application/json', ...originClaimHeaders({ url: target, method: 'POST', body: '{}' }) },
54
54
  body: '{}',
55
55
  signal: AbortSignal.timeout(SEND_TIMEOUT_MS),
56
56
  }).then(async (reply) => {
@@ -95,11 +95,13 @@ export function registerCanvasNotify(webServer, d) {
95
95
  res.writeHead(200, { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(text) });
96
96
  res.end(text);
97
97
  };
98
- // The ping has no content: drain a small body without buffering it,
99
- // and cut off anything larger.
98
+ // Retain only bounded raw ping bytes for Sig3 verification; cut off oversized uploads.
100
99
  let size = 0;
101
- req.on('data', (chunk) => { size += chunk.length; if (size > 4 * 1024)
102
- req.destroy(); });
100
+ const chunks = [];
101
+ req.on('data', (chunk) => { size += chunk.length; if (size > 4 * 1024) {
102
+ req.destroy();
103
+ return;
104
+ } chunks.push(chunk); });
103
105
  req.on('end', () => {
104
106
  void (async () => {
105
107
  if (req.method !== 'POST') {
@@ -115,7 +117,7 @@ export function registerCanvasNotify(webServer, d) {
115
117
  send({ ok: false, error: 'caller is not a tracked peer of this host' });
116
118
  return;
117
119
  }
118
- const claim = await verifyOriginClaim(req);
120
+ const claim = await verifyOriginClaim(req, { rawBody: Buffer.concat(chunks) });
119
121
  if (!claim.ok) {
120
122
  send({ ok: false, error: `untrusted-origin: OriginClaim verification failed (${claim.reason})` });
121
123
  return;
package/lib/faces/wire.js CHANGED
@@ -53,7 +53,7 @@ export function settlerClaimFields(claim, originOf) {
53
53
  * @param d - the captures listed on {@link WireDeps}.
54
54
  */
55
55
  export function registerWireFaces(webServer, d) {
56
- const { ctx, logger, config, cliDeposits, canvasStore, canvasNameReserved, sessionNodes, joinedSessions, idempotencyStore, verifyOriginClaim, remoteCliProof, callerLinkedToThisHost, recordActivity, secretEqual, a2aCallerRefusal, a2aCallerAdmission, allowlistVersionNow, observeAuthorization, scanSessionLogList, readSessionLogTail, taskLedger, refuseIfFlooded, refuseIfQueueFull, settleAndAnnounce, session, resolveAgentForTeam, parseCanvasTeamName, canvasLiveAgent, wakeColdTeam, probeConsumption, sessionTeamOf, localSessionOfHandle, steerRelay, fromClause, sanitizeFromClause, armReceiptAutosend, eventCountOf, receiptExpectationOf, armAsyncNudge, liveAgent, callerIsAgent, selfRouteText, noWakeFaceText, archivedColdMatchText, noColdMatchText, nativeTeamsPrepare, nativeTeamsRound, routeIntoAgent, } = d;
56
+ const { ctx, logger, config, cliDeposits, canvasStore, canvasNameReserved, sessionNodes, joinedSessions, idempotencyStore, verifyOriginClaim, remoteCliProof, callerLinkedToThisHost, recordActivity, secretEqual, a2aCallerRefusal, a2aCallerAdmission, allowlistVersionNow, observeAuthorization, scanSessionLogList, readSessionLogTail, taskLedger, refuseIfFlooded, refuseIfQueueFull, settleAndAnnounce, waitForForwardBirth, session, resolveAgentForTeam, parseCanvasTeamName, canvasLiveAgent, wakeColdTeam, probeConsumption, sessionTeamOf, localSessionOfHandle, steerRelay, fromClause, sanitizeFromClause, armReceiptAutosend, eventCountOf, receiptExpectationOf, armAsyncNudge, liveAgent, callerIsAgent, selfRouteText, noWakeFaceText, archivedColdMatchText, noColdMatchText, nativeTeamsPrepare, nativeTeamsRound, routeIntoAgent, } = d;
57
57
  // gh#45 CLI 户口卡面 (peer face, unsigned card serve): the deposited
58
58
  // CLI's SIGNED card is served verbatim under its own base URL — an
59
59
  // OriginClaim verification of a CLI envelope fetches exactly here
@@ -111,8 +111,13 @@ export function registerWireFaces(webServer, d) {
111
111
  req.on('end', () => {
112
112
  void (async () => {
113
113
  try {
114
- const body = JSON.parse(Buffer.concat(chunks).toString('utf8'));
115
- const claim = await verifyOriginClaim(req);
114
+ if (size > 16 * 1024) {
115
+ send(413, JSON.stringify({ ok: false, error: 'payload too large' }));
116
+ return;
117
+ }
118
+ const rawBody = Buffer.concat(chunks);
119
+ const body = JSON.parse(rawBody.toString('utf8'));
120
+ const claim = await verifyOriginClaim(req, { rawBody });
116
121
  if (!claim.ok) {
117
122
  logger.warn(`a2a: canvas-op claim failed (${claim.reason}) — caller=${String(req.headers['x-a2a-caller'] ?? '')} url=${String(req.headers['x-a2a-caller-url'] ?? '')} ts=${String(req.headers['x-a2a-caller-ts'] ?? '')}`);
118
123
  send(200, JSON.stringify({ ok: false, error: `untrusted-origin: OriginClaim verification failed (${claim.reason}) — canvas regulation requires a signed envelope`, code: -32000 }));
@@ -402,9 +407,10 @@ export function registerWireFaces(webServer, d) {
402
407
  }
403
408
  return;
404
409
  }
410
+ const rawBody = Buffer.concat(chunks);
405
411
  let body;
406
412
  try {
407
- body = JSON.parse(Buffer.concat(chunks).toString('utf8'));
413
+ body = JSON.parse(rawBody.toString('utf8'));
408
414
  if (body === null || typeof body !== 'object' || Array.isArray(body))
409
415
  throw new TypeError('direct body must be a JSON object'); // #168: `null` read body.team in this async 'end' callback → unhandled rejection → host exit
410
416
  }
@@ -474,7 +480,7 @@ export function registerWireFaces(webServer, d) {
474
480
  // F1: only a FOREIGN CLI handle needs a bound host claim and live home card
475
481
  // before admission. Every other caller keeps the existing admission-first order.
476
482
  const foreignCli = config.teamScopeRouting && CLI_HANDLE_RE.test(caller) && cliDeposits.get(caller) === undefined;
477
- const earlyClaim = foreignCli ? await verifyOriginClaim(req, { cli: true }) : undefined;
483
+ const earlyClaim = foreignCli ? await verifyOriginClaim(req, { cli: true, rawBody }) : undefined;
478
484
  const cliProof = foreignCli && earlyClaim?.ok ? await remoteCliProof(caller, earlyClaim).catch(() => undefined) : undefined;
479
485
  const admission = a2aCallerAdmission(caller, team, cliProof);
480
486
  const callerRefusal = refusalOf(admission);
@@ -496,7 +502,7 @@ export function registerWireFaces(webServer, d) {
496
502
  // after the window closes it rejects like a failed signature —
497
503
  // N3 proves a NAME's linkability, never key possession.
498
504
  // gh#133: the delivery face is where a deposited CLI identity signs its routes.
499
- const claim = earlyClaim ?? await verifyOriginClaim(req, { cli: true });
505
+ const claim = earlyClaim ?? await verifyOriginClaim(req, { cli: true, rawBody });
500
506
  if (!claim.ok) {
501
507
  if (claim.reason !== 'absent' || config.originClaimWindow === false) {
502
508
  recordActivity('in', team, caller, false);
@@ -553,6 +559,8 @@ export function registerWireFaces(webServer, d) {
553
559
  // #114 S4: the settler is this delivery's caller, as proven here. A CLI identity's claim URL
554
560
  // is not a host's origin (its origin is unconstrained), so it never vouches for one (review of PR #140 P1-2).
555
561
  // #139: the claim's card key names the host whatever address spelling it signed with.
562
+ // The response can race a fast peer receipt. Wait for this exact forwarding birth before judging or acknowledging it.
563
+ await waitForForwardBirth(message);
556
564
  const refusedSettlement = settleAndAnnounce(message, { caller, proof: callerProof, ...settlerClaimFields(claim, originOf), ingress: 'wire' });
557
565
  // #139 (design §4.4): every answer this delivery gets carries the refusal.
558
566
  if (refusedSettlement !== undefined)
@@ -582,12 +590,14 @@ export function registerWireFaces(webServer, d) {
582
590
  d.touchWakeDemand();
583
591
  const forwardMiss = async (original) => {
584
592
  const result = await d.forwardDirect({ team, message, caller, taskId, contextId, noWait,
593
+ auth: buildAuthorization(admission, { caller, proof: callerProof, ...(claim.ok ? { claim: { caller: claim.caller, origin: originOf(claim.callerUrl) } } : {}), ingress: 'wire', allowlistVersion: admittedAt.version, target: team, receiptTarget: caller, receivedAt: admittedAt.at }),
585
594
  localCliProof: claim.ok && claim.cli === true && claim.bound && claim.caller === caller, guarded: req.headers['x-a2a-guard'] !== undefined }).catch((error) => ({ ok: false, error: `forwarding unavailable: ${String(error)}`, code: -32000, task_id: taskId }));
595
+ const failure = result.ok ? '' : await d.diagnoseCanvas(team, original + ' — ' + result.error, claim.ok && claim.cli === true && claim.bound && claim.caller === caller ? caller : undefined);
586
596
  if (!result.ok && result.code !== -32005)
587
- idempotencyStore.recordOutcome(taskId, { status: 'failed', error: original + ' — ' + result.error });
597
+ idempotencyStore.recordOutcome(taskId, { status: 'failed', error: failure });
588
598
  else if (result.ok && result.task_status === 'TASK_STATE_COMPLETED')
589
599
  idempotencyStore.recordOutcome(taskId, { status: 'completed', reply: result.reply });
590
- const payload = JSON.stringify(result.ok ? { routed: true, ...(result.task_status === 'TASK_STATE_DELIVERED' ? { delivered: true } : { result: { text: result.reply } }), team, task_id: taskId, context_id: contextId, task_status: result.task_status, consumed: result.consumed, bridge: result.bridge, receipt: result.receipt, forwarded: true } : { error: original + ' — ' + result.error, code: result.code ?? -32000, receipt: result.receipt, task_id: taskId, team, task_status: result.code === -32005 ? 'TASK_STATE_DELIVERED' : 'TASK_STATE_FAILED' });
600
+ const payload = JSON.stringify(result.ok ? { routed: true, ...(result.task_status === 'TASK_STATE_DELIVERED' ? { delivered: true } : { result: { text: result.reply } }), team, task_id: taskId, context_id: contextId, task_status: result.task_status, consumed: result.consumed, bridge: result.bridge, receipt: result.receipt, forwarded: true } : { error: failure, code: result.code ?? -32000, receipt: result.receipt, task_id: taskId, team, task_status: result.code === -32005 ? 'TASK_STATE_DELIVERED' : 'TASK_STATE_FAILED' });
591
601
  res.writeHead(200, { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) });
592
602
  res.end(payload);
593
603
  };
@@ -803,7 +813,16 @@ export function registerWireFaces(webServer, d) {
803
813
  })();
804
814
  return;
805
815
  }
806
- void routeIntoAgent(team, message, caller, taskId, proofClause, directHeader).then(async (outcome) => {
816
+ let syncTarget;
817
+ // A forwarded synchronous delivery still owes its automatic receipt after the HTTP waiter expires.
818
+ const syncSteered = req.headers['x-a2a-guard'] === undefined || parseReceipt(message) !== null ? undefined : (target, floor) => {
819
+ syncTarget = sessionTeamOf(target);
820
+ const receiptTarget = callback !== '' ? callback : caller;
821
+ const auth = buildAuthorization(admission, { caller, proof: callerProof, ...(claim.ok ? { claim: { caller: claim.caller, origin: originOf(claim.callerUrl) } } : {}), ...(admission.rule === 'remote-ref-cli' && claim.ok ? { carrierUrl: originOf(claim.callerUrl) } : {}), ingress: 'wire', allowlistVersion: admittedAt.version, target: team, receiptTarget, receivedAt: admittedAt.at });
822
+ taskLedger.trackInbound(taskId, team, receiptTarget, contextId, receiptExpectationOf(receiptTarget) !== 'process-label', briefOf(message), auth, syncTarget === team ? undefined : syncTarget);
823
+ armReceiptAutosend(target, taskId, receiptTarget, message, floor);
824
+ };
825
+ void routeIntoAgent(team, message, caller, taskId, proofClause, directHeader, undefined, syncSteered).then(async (outcome) => {
807
826
  if (!outcome.ok && outcome.localMiss) {
808
827
  await forwardMiss(outcome.error);
809
828
  return;
@@ -834,7 +853,7 @@ export function registerWireFaces(webServer, d) {
834
853
  team,
835
854
  session,
836
855
  result: { text: outcome.reply },
837
- ...(req.headers['x-a2a-guard'] !== undefined ? ((target) => target === undefined ? {} : { delivered_to: sessionTeamOf(target) })(resolveAgentForTeam(team) ?? canvasLiveAgent(parseCanvasTeamName(team))) : {}),
856
+ ...(syncTarget !== undefined ? { delivered_to: syncTarget } : {}),
838
857
  task_id: taskId,
839
858
  context_id: contextId,
840
859
  task_status: outcome.status ?? 'TASK_STATE_COMPLETED',