@nelsonlongxiang/dsh-open-a2a-net 0.6.34 → 0.6.35

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.
@@ -18,7 +18,7 @@ html,body{margin:0;height:100%;background:var(--n-bg);color:var(--n-ink);
18
18
  font-family:"Segoe UI",system-ui,sans-serif;}
19
19
  #app{width:100%;height:100%;position:relative}
20
20
  </style>
21
- <script type="module" crossorigin src="./assets/index-D2BGWOeU.js"></script>
21
+ <script type="module" crossorigin src="./assets/index-Du2E6DJL.js"></script>
22
22
  <link rel="stylesheet" crossorigin href="./assets/index-z33lHtLw.css">
23
23
  </head>
24
24
  <body>
package/cli/a2a.mjs CHANGED
@@ -959,13 +959,15 @@ async function main() {
959
959
  const pending = s.tasks ?? []
960
960
  const dead = s.tasksDead ?? []
961
961
  const archived = s.archived ?? []
962
- if (has('--json')) { console.log(JSON.stringify({ pending, dead, archived }, null, 2)); return }
962
+ if (has('--json')) { console.log(JSON.stringify({ pending, dead, archived, ...(s.settlement === undefined ? {} : { settlement: s.settlement }), ...(s.conservation === undefined ? {} : { conservation: s.conservation }) }, null, 2)); return }
963
963
  console.log(`pending (${String(pending.length)}):`)
964
964
  listCapped(pending, 20, t => ` ${String(t.taskId)} → ${String(t.team)} ${String(t.direction ?? '')}${senderTail(t)}`)
965
965
  console.log(`dead (${String(dead.length)}):`)
966
966
  listCapped(dead, 20, t => ` ${String(t.taskId)} → ${String(t.team)} (${String(t.reason ?? '?')})${senderTail(t)}`)
967
967
  console.log(`archived (${String(archived.length)}):`)
968
968
  listCapped(archived, 20, t => ` ${String(t.taskId)} ${String(t.outcome ?? '?')}${t.endedBy !== undefined && t.endedBy !== 'receipt' ? ` (${String(t.endedBy)})` : ''}${senderTail(t)}`)
969
+ if (s.conservation != null) console.log(`conservation: conserved=${String(s.conservation.conserved)} delta=${String(s.conservation.delta)}`)
970
+ if (s.settlement != null) console.log(`settlement: mode=${String(s.settlement.mode)} refusalsTotal=${String(s.settlement.refusalsTotal)} offChain=${String(s.settlement.offChain)}`)
969
971
  return
970
972
  }
971
973
  if (cmd === 'status') {
@@ -64,6 +64,7 @@ export class CanvasStore {
64
64
  /** Team entries in creation order; members in add order (routing priority). */
65
65
  teams = [];
66
66
  cliHome = {};
67
+ cliHomeCleared = new Set();
67
68
  path;
68
69
  listeners = new Set();
69
70
  /**
@@ -111,6 +112,11 @@ export class CanvasStore {
111
112
  this.cliHome[handle] = home;
112
113
  }
113
114
  }
115
+ if (Array.isArray(snapshot.cliHomeCleared))
116
+ for (const handle of snapshot.cliHomeCleared) {
117
+ if (typeof handle === 'string' && CLI_HANDLE_RE.test(handle) && this.cliHome[handle] === undefined && this.teams.some(t => t.members.includes(handle)))
118
+ this.cliHomeCleared.add(handle);
119
+ }
114
120
  }
115
121
  catch {
116
122
  // A corrupt store is not fatal: the user re-creates teams; routing
@@ -132,7 +138,7 @@ export class CanvasStore {
132
138
  return;
133
139
  try {
134
140
  mkdirSync(dirname(this.path), { recursive: true });
135
- const snapshot = { teams: this.teams.map(t => ({ name: t.name, members: [...t.members] })), ...(Object.keys(this.cliHome).length > 0 ? { cliHome: { ...this.cliHome } } : {}) };
141
+ const snapshot = { teams: this.teams.map(t => ({ name: t.name, members: [...t.members] })), ...(Object.keys(this.cliHome).length > 0 ? { cliHome: { ...this.cliHome } } : {}), ...(this.cliHomeCleared.size > 0 ? { cliHomeCleared: [...this.cliHomeCleared] } : {}) };
136
142
  writeFileSync(this.path, JSON.stringify(snapshot), { mode: 0o600 });
137
143
  }
138
144
  catch {
@@ -156,6 +162,15 @@ export class CanvasStore {
156
162
  /** Frozen, operator-linked CLI home; undefined means unknown or ambiguous. */
157
163
  cliHomeOf(handle) { return this.cliHome[handle]; }
158
164
  pinnedCliHomes() { return Object.entries(this.cliHome); }
165
+ wasCliHomeCleared(handle) { return this.cliHomeCleared.has(handle); }
166
+ clearCliHomeForLocal(handle) {
167
+ if (this.cliHome[handle] === undefined)
168
+ return false;
169
+ delete this.cliHome[handle];
170
+ this.cliHomeCleared.add(handle);
171
+ this.persist();
172
+ return true;
173
+ }
159
174
  /**
160
175
  * The named team's remote member refs (zone-qualified team handles) in
161
176
  * routing-priority order — the failover tier after the local live/cold
@@ -218,6 +233,7 @@ export class CanvasStore {
218
233
  if (entry.members.includes(id)) {
219
234
  if (home !== undefined && this.cliHome[id] !== home) {
220
235
  this.cliHome[id] = home;
236
+ this.cliHomeCleared.delete(id);
221
237
  this.persist();
222
238
  }
223
239
  return true;
@@ -225,8 +241,10 @@ export class CanvasStore {
225
241
  if (entry.members.length >= CANVAS_MEMBER_CAP)
226
242
  return false;
227
243
  entry.members.push(id);
228
- if (home !== undefined)
244
+ if (home !== undefined) {
229
245
  this.cliHome[id] = home;
246
+ this.cliHomeCleared.delete(id);
247
+ }
230
248
  this.persist();
231
249
  return true;
232
250
  }
@@ -246,8 +264,10 @@ export class CanvasStore {
246
264
  return true;
247
265
  }
248
266
  forgetUnusedHome(id) {
249
- if (!this.teams.some(team => team.members.includes(id)))
267
+ if (!this.teams.some(team => team.members.includes(id))) {
250
268
  delete this.cliHome[id];
269
+ this.cliHomeCleared.delete(id);
270
+ }
251
271
  }
252
272
  /**
253
273
  * Drop one id from every team — the leave and archive paths: a session
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.34-474a60a\src\src\client\A2aControl.module.css.mjs
26
- const css = ".YPQWgG_wrap{max-width:100%;display:inline-flex;position:relative}.YPQWgG_trigger{align-items:center;gap:4px;display:inline-flex;position:relative}.YPQWgG_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 YPQWgG_badgePulse;position:absolute;top:-6px;right:-8px}@keyframes YPQWgG_badgePulse{0%,to{box-shadow:0 0 #4c8bf573}50%{box-shadow:0 0 0 4px #4c8bf500}}@media (prefers-reduced-motion:reduce){.YPQWgG_unreadBadge{animation:none}}.YPQWgG_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}.YPQWgG_title{font-size:13px;font-weight:600}.YPQWgG_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}.YPQWgG_stageRow{border-top:1px solid var(--dsh-border,#2a2a30);align-items:center;gap:8px;margin-top:4px;padding-top:8px;display:flex}.YPQWgG_stageTitle{text-transform:uppercase;letter-spacing:.04em;opacity:.65;font-size:11px;font-weight:600}.YPQWgG_stageLinks{gap:6px;display:flex}.YPQWgG_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}.YPQWgG_stageLink:hover,.YPQWgG_stageLink:focus-visible{border-color:var(--dsh-accent,#4c8bf5)}.YPQWgG_empty{opacity:.7;font-size:12px}.YPQWgG_row{border:1px solid #0000;border-radius:8px;align-items:center;gap:10px;padding:8px;display:flex}.YPQWgG_joined{border-color:var(--dsh-border,#2a2a30)}.YPQWgG_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}.YPQWgG_facts:hover{background:var(--dsh-surface-hover,#ffffff0f)}.YPQWgG_nameRow{align-items:center;gap:6px;display:flex}.YPQWgG_stateDot{border-radius:50%;flex:none;width:7px;height:7px}.YPQWgG_stateDot.YPQWgG_live{background:var(--dsh-accent,#4c8bf5)}.YPQWgG_stateDot.YPQWgG_live.YPQWgG_joined{background:var(--dsh-success,#3fb96f)}.YPQWgG_stateDot.YPQWgG_cold{background:var(--dsh-warning,#d9a13c)}.YPQWgG_name{text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500;overflow:hidden}.YPQWgG_description{opacity:.7;text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}.YPQWgG_team{opacity:.55;font-size:11px;font-family:var(--dsh-mono,monospace)}.YPQWgG_note{opacity:.6;font-size:11px}.YPQWgG_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}.YPQWgG_searchInput:focus{outline:1px solid var(--dsh-accent,#4c8bf5)}.YPQWgG_sessionGroup{flex-direction:column;gap:2px;display:flex}.YPQWgG_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}.YPQWgG_groupChevron{transition:transform .12s;display:inline-block}.YPQWgG_groupChevronClosed{transform:rotate(-90deg)}.YPQWgG_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}.YPQWgG_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}.YPQWgG_groupButton:hover{border-color:var(--dsh-border,#2a2a30);opacity:1}.YPQWgG_groupPicker{border:1px dashed var(--dsh-border,#2a2a30);border-radius:8px;flex-wrap:wrap;gap:6px;width:100%;padding:8px;display:flex;position:relative}.YPQWgG_groupOption{border:1px solid var(--dsh-border,#2a2a30);color:inherit;cursor:pointer;background:0 0;border-radius:999px;padding:2px 8px;font-size:11px}.YPQWgG_groupOptionActive{border-color:var(--dsh-accent,#4c8bf5);color:var(--dsh-accent,#4c8bf5)}.YPQWgG_groupNew{align-items:center;gap:6px;width:100%;margin-top:2px;display:flex}.YPQWgG_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}.YPQWgG_peerGrid{flex-wrap:wrap;gap:6px;display:flex}.YPQWgG_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}.YPQWgG_peerHost{text-overflow:ellipsis;white-space:nowrap;max-width:160px;overflow:hidden}.YPQWgG_peerScore{font-family:var(--dsh-mono,monospace);opacity:.6}.YPQWgG_activityList{flex-direction:column;gap:2px;display:flex}.YPQWgG_activityRow{border-radius:4px;align-items:center;gap:8px;padding:2px 4px;font-size:11px;display:flex}.YPQWgG_activityRow[data-ok=false]{opacity:.55}.YPQWgG_activityDir{flex:none;font-weight:600}.YPQWgG_activityDir.YPQWgG_in{color:var(--dsh-success,#3fb96f)}.YPQWgG_activityDir.YPQWgG_out{color:var(--dsh-accent,#4c8bf5)}.YPQWgG_activityTeam{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-family:var(--dsh-mono,monospace);opacity:.8;flex:1;overflow:hidden}.YPQWgG_activityPeer{text-overflow:ellipsis;white-space:nowrap;opacity:.55;max-width:90px;overflow:hidden}.YPQWgG_activityTime{opacity:.5;font-family:var(--dsh-mono,monospace);flex:none}.YPQWgG_activityRow[data-jump=true]{cursor:pointer}.YPQWgG_activityRow[data-jump=true]:hover{background:var(--dsh-surface-hover,#ffffff0f)}.YPQWgG_fresh{animation:1.2s ease-out 3 YPQWgG_freshFlash}@keyframes YPQWgG_freshFlash{0%{background:#4c8bf538}to{background:0 0}}@media (prefers-reduced-motion:reduce){.YPQWgG_fresh{background:#4c8bf51f;animation:none}}.YPQWgG_inFlightList{flex-direction:column;gap:2px;margin-bottom:4px;display:flex}.YPQWgG_inFlightRow{border:1px dashed var(--dsh-border,#2a2a30);border-radius:4px;align-items:center;gap:8px;padding:3px 4px;font-size:11px;display:flex}.YPQWgG_inFlightPulse{background:var(--dsh-accent,#4c8bf5);border-radius:50%;flex:none;width:7px;height:7px;animation:1.1s ease-in-out infinite YPQWgG_flightPulse}@keyframes YPQWgG_flightPulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.55)}}@media (prefers-reduced-motion:reduce){.YPQWgG_inFlightPulse{animation:none}}.YPQWgG_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}.YPQWgG_inFlightStale{opacity:.55}.YPQWgG_canvasCreate{align-items:center;gap:6px;margin-bottom:4px;display:flex}.YPQWgG_canvasCreate .YPQWgG_groupInput{flex:1;min-width:0}.YPQWgG_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}.YPQWgG_canvasChip>span:not([class]){white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}.YPQWgG_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}.YPQWgG_canvasChipRemove:hover{color:var(--dsh-color-text-primary,#eee)}.YPQWgG_canvasChipRemove{transition:color .14s ease-out}@media (prefers-reduced-motion:reduce){.YPQWgG_canvasChipRemove{transition:none}}.YPQWgG_canvasChipRemove:focus-visible{outline:2px solid var(--dsh-color-primary,#4f8cff);outline-offset:1px;border-radius:4px}.YPQWgG_canvasActions{gap:6px;margin-top:4px;display:flex}.YPQWgG_inFlightStale .YPQWgG_inFlightPulse{opacity:.4;animation:none}.YPQWgG_failMark{color:#f87171;flex-shrink:0;font-size:11px}.YPQWgG_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.35-d5a66d9\src\src\client\A2aControl.module.css.mjs
26
+ const css = "._9zk42q_wrap{max-width:100%;display:inline-flex;position:relative}._9zk42q_trigger{align-items:center;gap:4px;display:inline-flex;position:relative}._9zk42q_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 _9zk42q_badgePulse;position:absolute;top:-6px;right:-8px}@keyframes _9zk42q_badgePulse{0%,to{box-shadow:0 0 #4c8bf573}50%{box-shadow:0 0 0 4px #4c8bf500}}@media (prefers-reduced-motion:reduce){._9zk42q_unreadBadge{animation:none}}._9zk42q_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}._9zk42q_title{font-size:13px;font-weight:600}._9zk42q_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}._9zk42q_stageRow{border-top:1px solid var(--dsh-border,#2a2a30);align-items:center;gap:8px;margin-top:4px;padding-top:8px;display:flex}._9zk42q_stageTitle{text-transform:uppercase;letter-spacing:.04em;opacity:.65;font-size:11px;font-weight:600}._9zk42q_stageLinks{gap:6px;display:flex}._9zk42q_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}._9zk42q_stageLink:hover,._9zk42q_stageLink:focus-visible{border-color:var(--dsh-accent,#4c8bf5)}._9zk42q_empty{opacity:.7;font-size:12px}._9zk42q_row{border:1px solid #0000;border-radius:8px;align-items:center;gap:10px;padding:8px;display:flex}._9zk42q_joined{border-color:var(--dsh-border,#2a2a30)}._9zk42q_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}._9zk42q_facts:hover{background:var(--dsh-surface-hover,#ffffff0f)}._9zk42q_nameRow{align-items:center;gap:6px;display:flex}._9zk42q_stateDot{border-radius:50%;flex:none;width:7px;height:7px}._9zk42q_stateDot._9zk42q_live{background:var(--dsh-accent,#4c8bf5)}._9zk42q_stateDot._9zk42q_live._9zk42q_joined{background:var(--dsh-success,#3fb96f)}._9zk42q_stateDot._9zk42q_cold{background:var(--dsh-warning,#d9a13c)}._9zk42q_name{text-overflow:ellipsis;white-space:nowrap;font-size:13px;font-weight:500;overflow:hidden}._9zk42q_description{opacity:.7;text-overflow:ellipsis;white-space:nowrap;font-size:12px;overflow:hidden}._9zk42q_team{opacity:.55;font-size:11px;font-family:var(--dsh-mono,monospace)}._9zk42q_note{opacity:.6;font-size:11px}._9zk42q_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}._9zk42q_searchInput:focus{outline:1px solid var(--dsh-accent,#4c8bf5)}._9zk42q_sessionGroup{flex-direction:column;gap:2px;display:flex}._9zk42q_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}._9zk42q_groupChevron{transition:transform .12s;display:inline-block}._9zk42q_groupChevronClosed{transform:rotate(-90deg)}._9zk42q_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}._9zk42q_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}._9zk42q_groupButton:hover{border-color:var(--dsh-border,#2a2a30);opacity:1}._9zk42q_groupPicker{border:1px dashed var(--dsh-border,#2a2a30);border-radius:8px;flex-wrap:wrap;gap:6px;width:100%;padding:8px;display:flex;position:relative}._9zk42q_groupOption{border:1px solid var(--dsh-border,#2a2a30);color:inherit;cursor:pointer;background:0 0;border-radius:999px;padding:2px 8px;font-size:11px}._9zk42q_groupOptionActive{border-color:var(--dsh-accent,#4c8bf5);color:var(--dsh-accent,#4c8bf5)}._9zk42q_groupNew{align-items:center;gap:6px;width:100%;margin-top:2px;display:flex}._9zk42q_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}._9zk42q_peerGrid{flex-wrap:wrap;gap:6px;display:flex}._9zk42q_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}._9zk42q_peerHost{text-overflow:ellipsis;white-space:nowrap;max-width:160px;overflow:hidden}._9zk42q_peerScore{font-family:var(--dsh-mono,monospace);opacity:.6}._9zk42q_activityList{flex-direction:column;gap:2px;display:flex}._9zk42q_activityRow{border-radius:4px;align-items:center;gap:8px;padding:2px 4px;font-size:11px;display:flex}._9zk42q_activityRow[data-ok=false]{opacity:.55}._9zk42q_activityDir{flex:none;font-weight:600}._9zk42q_activityDir._9zk42q_in{color:var(--dsh-success,#3fb96f)}._9zk42q_activityDir._9zk42q_out{color:var(--dsh-accent,#4c8bf5)}._9zk42q_activityTeam{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-family:var(--dsh-mono,monospace);opacity:.8;flex:1;overflow:hidden}._9zk42q_activityPeer{text-overflow:ellipsis;white-space:nowrap;opacity:.55;max-width:90px;overflow:hidden}._9zk42q_activityTime{opacity:.5;font-family:var(--dsh-mono,monospace);flex:none}._9zk42q_activityRow[data-jump=true]{cursor:pointer}._9zk42q_activityRow[data-jump=true]:hover{background:var(--dsh-surface-hover,#ffffff0f)}._9zk42q_fresh{animation:1.2s ease-out 3 _9zk42q_freshFlash}@keyframes _9zk42q_freshFlash{0%{background:#4c8bf538}to{background:0 0}}@media (prefers-reduced-motion:reduce){._9zk42q_fresh{background:#4c8bf51f;animation:none}}._9zk42q_inFlightList{flex-direction:column;gap:2px;margin-bottom:4px;display:flex}._9zk42q_inFlightRow{border:1px dashed var(--dsh-border,#2a2a30);border-radius:4px;align-items:center;gap:8px;padding:3px 4px;font-size:11px;display:flex}._9zk42q_inFlightPulse{background:var(--dsh-accent,#4c8bf5);border-radius:50%;flex:none;width:7px;height:7px;animation:1.1s ease-in-out infinite _9zk42q_flightPulse}@keyframes _9zk42q_flightPulse{0%,to{opacity:1;transform:scale(1)}50%{opacity:.5;transform:scale(.55)}}@media (prefers-reduced-motion:reduce){._9zk42q_inFlightPulse{animation:none}}._9zk42q_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}._9zk42q_inFlightStale{opacity:.55}._9zk42q_canvasCreate{align-items:center;gap:6px;margin-bottom:4px;display:flex}._9zk42q_canvasCreate ._9zk42q_groupInput{flex:1;min-width:0}._9zk42q_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}._9zk42q_canvasChip>span:not([class]){white-space:nowrap;text-overflow:ellipsis;min-width:0;overflow:hidden}._9zk42q_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}._9zk42q_canvasChipRemove:hover{color:var(--dsh-color-text-primary,#eee)}._9zk42q_canvasChipRemove{transition:color .14s ease-out}@media (prefers-reduced-motion:reduce){._9zk42q_canvasChipRemove{transition:none}}._9zk42q_canvasChipRemove:focus-visible{outline:2px solid var(--dsh-color-primary,#4f8cff);outline-offset:1px;border-radius:4px}._9zk42q_canvasActions{gap:6px;margin-top:4px;display:flex}._9zk42q_inFlightStale ._9zk42q_inFlightPulse{opacity:.4;animation:none}._9zk42q_failMark{color:#f87171;flex-shrink:0;font-size:11px}._9zk42q_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": "YPQWgG_activityDir",
37
- "activityList": "YPQWgG_activityList",
38
- "activityPeer": "YPQWgG_activityPeer",
39
- "activityRow": "YPQWgG_activityRow",
40
- "activityTeam": "YPQWgG_activityTeam",
41
- "activityTime": "YPQWgG_activityTime",
42
- "badgePulse": "YPQWgG_badgePulse",
43
- "canvasActions": "YPQWgG_canvasActions",
44
- "canvasChip": "YPQWgG_canvasChip",
45
- "canvasChipRemove": "YPQWgG_canvasChipRemove",
46
- "canvasCreate": "YPQWgG_canvasCreate",
47
- "cold": "YPQWgG_cold",
48
- "description": "YPQWgG_description",
49
- "empty": "YPQWgG_empty",
50
- "facts": "YPQWgG_facts",
51
- "failMark": "YPQWgG_failMark",
52
- "flightPulse": "YPQWgG_flightPulse",
53
- "fresh": "YPQWgG_fresh",
54
- "freshFlash": "YPQWgG_freshFlash",
55
- "groupButton": "YPQWgG_groupButton",
56
- "groupChevron": "YPQWgG_groupChevron",
57
- "groupChevronClosed": "YPQWgG_groupChevronClosed",
58
- "groupHead": "YPQWgG_groupHead",
59
- "groupInput": "YPQWgG_groupInput",
60
- "groupNew": "YPQWgG_groupNew",
61
- "groupOption": "YPQWgG_groupOption",
62
- "groupOptionActive": "YPQWgG_groupOptionActive",
63
- "groupPicker": "YPQWgG_groupPicker",
64
- "groupTag": "YPQWgG_groupTag",
65
- "in": "YPQWgG_in",
66
- "inFlightList": "YPQWgG_inFlightList",
67
- "inFlightPulse": "YPQWgG_inFlightPulse",
68
- "inFlightRow": "YPQWgG_inFlightRow",
69
- "inFlightStale": "YPQWgG_inFlightStale",
70
- "joined": "YPQWgG_joined",
71
- "live": "YPQWgG_live",
72
- "name": "YPQWgG_name",
73
- "nameRow": "YPQWgG_nameRow",
74
- "note": "YPQWgG_note",
75
- "out": "YPQWgG_out",
76
- "peerChip": "YPQWgG_peerChip",
77
- "peerGrid": "YPQWgG_peerGrid",
78
- "peerHost": "YPQWgG_peerHost",
79
- "peerScore": "YPQWgG_peerScore",
80
- "popover": "YPQWgG_popover",
81
- "row": "YPQWgG_row",
82
- "searchInput": "YPQWgG_searchInput",
83
- "sectionTitle": "YPQWgG_sectionTitle",
84
- "sessionGroup": "YPQWgG_sessionGroup",
85
- "stageLink": "YPQWgG_stageLink",
86
- "stageLinks": "YPQWgG_stageLinks",
87
- "stageRow": "YPQWgG_stageRow",
88
- "stageTitle": "YPQWgG_stageTitle",
89
- "staleNote": "YPQWgG_staleNote",
90
- "stateDot": "YPQWgG_stateDot",
91
- "team": "YPQWgG_team",
92
- "title": "YPQWgG_title",
93
- "trigger": "YPQWgG_trigger",
94
- "unreadBadge": "YPQWgG_unreadBadge",
95
- "versionTag": "YPQWgG_versionTag",
96
- "wrap": "YPQWgG_wrap"
36
+ "activityDir": "_9zk42q_activityDir",
37
+ "activityList": "_9zk42q_activityList",
38
+ "activityPeer": "_9zk42q_activityPeer",
39
+ "activityRow": "_9zk42q_activityRow",
40
+ "activityTeam": "_9zk42q_activityTeam",
41
+ "activityTime": "_9zk42q_activityTime",
42
+ "badgePulse": "_9zk42q_badgePulse",
43
+ "canvasActions": "_9zk42q_canvasActions",
44
+ "canvasChip": "_9zk42q_canvasChip",
45
+ "canvasChipRemove": "_9zk42q_canvasChipRemove",
46
+ "canvasCreate": "_9zk42q_canvasCreate",
47
+ "cold": "_9zk42q_cold",
48
+ "description": "_9zk42q_description",
49
+ "empty": "_9zk42q_empty",
50
+ "facts": "_9zk42q_facts",
51
+ "failMark": "_9zk42q_failMark",
52
+ "flightPulse": "_9zk42q_flightPulse",
53
+ "fresh": "_9zk42q_fresh",
54
+ "freshFlash": "_9zk42q_freshFlash",
55
+ "groupButton": "_9zk42q_groupButton",
56
+ "groupChevron": "_9zk42q_groupChevron",
57
+ "groupChevronClosed": "_9zk42q_groupChevronClosed",
58
+ "groupHead": "_9zk42q_groupHead",
59
+ "groupInput": "_9zk42q_groupInput",
60
+ "groupNew": "_9zk42q_groupNew",
61
+ "groupOption": "_9zk42q_groupOption",
62
+ "groupOptionActive": "_9zk42q_groupOptionActive",
63
+ "groupPicker": "_9zk42q_groupPicker",
64
+ "groupTag": "_9zk42q_groupTag",
65
+ "in": "_9zk42q_in",
66
+ "inFlightList": "_9zk42q_inFlightList",
67
+ "inFlightPulse": "_9zk42q_inFlightPulse",
68
+ "inFlightRow": "_9zk42q_inFlightRow",
69
+ "inFlightStale": "_9zk42q_inFlightStale",
70
+ "joined": "_9zk42q_joined",
71
+ "live": "_9zk42q_live",
72
+ "name": "_9zk42q_name",
73
+ "nameRow": "_9zk42q_nameRow",
74
+ "note": "_9zk42q_note",
75
+ "out": "_9zk42q_out",
76
+ "peerChip": "_9zk42q_peerChip",
77
+ "peerGrid": "_9zk42q_peerGrid",
78
+ "peerHost": "_9zk42q_peerHost",
79
+ "peerScore": "_9zk42q_peerScore",
80
+ "popover": "_9zk42q_popover",
81
+ "row": "_9zk42q_row",
82
+ "searchInput": "_9zk42q_searchInput",
83
+ "sectionTitle": "_9zk42q_sectionTitle",
84
+ "sessionGroup": "_9zk42q_sessionGroup",
85
+ "stageLink": "_9zk42q_stageLink",
86
+ "stageLinks": "_9zk42q_stageLinks",
87
+ "stageRow": "_9zk42q_stageRow",
88
+ "stageTitle": "_9zk42q_stageTitle",
89
+ "staleNote": "_9zk42q_staleNote",
90
+ "stateDot": "_9zk42q_stateDot",
91
+ "team": "_9zk42q_team",
92
+ "title": "_9zk42q_title",
93
+ "trigger": "_9zk42q_trigger",
94
+ "unreadBadge": "_9zk42q_unreadBadge",
95
+ "versionTag": "_9zk42q_versionTag",
96
+ "wrap": "_9zk42q_wrap"
97
97
  };
98
98
  //#endregion
99
99
  //#region lib/client/A2aControl.js
@@ -152,7 +152,7 @@ export function registerAdminFaces(webServer, d) {
152
152
  rows.push({ id: localId, team: `${config.team}/${id8(localId)}`, joined: sessionNodes.has(localId) || joinedSessions.has(localId), live: sessionNodes.has(localId) });
153
153
  continue;
154
154
  }
155
- rows.push({ id, team: id, remote: true, ...(CLI_HANDLE_RE.test(id) ? { cliHome: canvasStore.cliHomeOf(id) ?? null, cliHeld: cliDeposits.directoryRows().some(row => row.team === id) } : {}) });
155
+ rows.push({ id, team: id, remote: true, ...(CLI_HANDLE_RE.test(id) ? { cliHome: canvasStore.cliHomeOf(id) ?? null, cliHeld: cliDeposits.directoryRows().some(row => row.team === id), ...(canvasStore.wasCliHomeCleared(id) ? { cliHomeCleared: true } : {}) } : {}) });
156
156
  continue;
157
157
  }
158
158
  if (seenLocal.has(id))
@@ -469,8 +469,9 @@ export function registerAdminFaces(webServer, d) {
469
469
  return '';
470
470
  } })();
471
471
  const out = name === config.team ? { ok: false, error: `a CLI identity may not be named after this host's zone "${config.team}" — choose another agent name` } : cliDeposits.deposit(card);
472
+ const homeCleared = out.ok && canvasStore.clearCliHomeForLocal(out.handle);
472
473
  payload = JSON.stringify(out.ok
473
- ? { ok: true, handle: out.handle, expiresAt: out.expiresAt, cardUrl: `${d.ownCardUrl}/a2a/cli/${encodeURIComponent(out.handle)}` }
474
+ ? { ok: true, handle: out.handle, expiresAt: out.expiresAt, ...(homeCleared ? { homeCleared: true } : {}), cardUrl: `${d.ownCardUrl}/a2a/cli/${encodeURIComponent(out.handle)}` }
474
475
  : { ok: false, error: out.error });
475
476
  }
476
477
  else if (action === 'revoke') {
package/lib/faces/wire.js CHANGED
@@ -7,6 +7,7 @@ import { briefOf } from "../task-ledger.js";
7
7
  import { parseReceipt, taskIdRefusal } from "../receipt.js";
8
8
  import { buildAuthorization, claimCovers, refusalOf } from "../task-auth.js";
9
9
  import { isRootOriginUrl } from "../origin-claim.js";
10
+ import { withResponseFields } from "../response-fields.js";
10
11
  import { WIRE_ERROR_PAYLOAD_TOO_LARGE, withinRouteBodyCap } from "../transport-caps.js";
11
12
  /**
12
13
  * #139 (design §4.4): add `settlement: {status:'refused', reason, task_id}`
@@ -16,30 +17,7 @@ import { WIRE_ERROR_PAYLOAD_TOO_LARGE, withinRouteBodyCap } from "../transport-c
16
17
  * body is sent unchanged.
17
18
  */
18
19
  export function withSettlementField(res, settlement) {
19
- const writeHead = res.writeHead.bind(res);
20
- const end = res.end.bind(res);
21
- let head;
22
- res.writeHead = (status, headers) => {
23
- head = { status, headers: { ...(headers ?? {}) } };
24
- return res;
25
- };
26
- res.end = (body) => {
27
- let out = body;
28
- if (typeof body === 'string') {
29
- try {
30
- const parsed = JSON.parse(body);
31
- if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed))
32
- out = JSON.stringify({ ...parsed, settlement });
33
- }
34
- catch { /* not JSON: unchanged */ }
35
- }
36
- if (head !== undefined) {
37
- if (out !== undefined && 'Content-Length' in head.headers)
38
- head.headers['Content-Length'] = Buffer.byteLength(out);
39
- writeHead(head.status, head.headers);
40
- }
41
- return end(out);
42
- };
20
+ withResponseFields(res, { settlement });
43
21
  }
44
22
  /** The origin of a card URL for the authorization record; the raw text, bounded, when it does not parse. */
45
23
  const originOf = (url) => {
@@ -1,3 +1,6 @@
1
+ import { withResponseFields } from "./response-fields.js";
2
+ export const FILE_CHANNEL_ADVICE = { reason: 'use-file-channel', message: 'Files must use the file channel, not session messages: a2a_file_send { paths, team }; CLI: a2a-net file send <path…> <team>.' };
3
+ export const fileChannelAdvice = (message) => message.includes('[JPFILE-PART') ? FILE_CHANNEL_ADVICE : undefined;
1
4
  export const WIRE_ERROR_RATE_LIMITED = -32008;
2
5
  export const MAX_PARTS_PER_TRANSFER = 16;
3
6
  // Matches the current L9 threshold; per-sender across ALL targets is stricter.
@@ -92,10 +95,13 @@ export class FloodGuard {
92
95
  export function createFloodGuard(outcomes, recordActivity) {
93
96
  const guard = new FloodGuard();
94
97
  return (caller, message, team, taskId, res) => {
98
+ const advice = fileChannelAdvice(message);
99
+ if (advice !== undefined)
100
+ withResponseFields(res, { advice });
95
101
  const reason = guard.check(caller, message);
96
102
  if (reason === undefined)
97
103
  return false;
98
- const error = 'flood-guard: ' + reason + ' (gh#91): file data must use an independent channel; no delivery occurred';
104
+ const error = 'flood-guard: ' + reason + ' (gh#91): file data must use an independent channel; no delivery occurred' + (advice === undefined ? '' : '; use-file-channel: ' + advice.message);
99
105
  outcomes.recordOutcome(taskId, { status: 'failed', error });
100
106
  recordActivity('in', team, caller, false);
101
107
  const payload = JSON.stringify({ error, reason, code: WIRE_ERROR_RATE_LIMITED, task_id: taskId, team, delivered: false, task_status: 'TASK_STATE_FAILED' });
package/lib/index.js CHANGED
@@ -762,7 +762,10 @@ export function apply(ctx, config) {
762
762
  // message that still echoes a pending task id means the peer
763
763
  // demonstrably responded to it, which settles the debt even when the
764
764
  // receipt formality never rode the reply.
765
- const echoed = taskLedger.resolveEcho(message, judge);
765
+ // #125 (owner data 2026-11-03: two production rows settled by a relay and by another task's receipt):
766
+ // an echo is weak evidence, so under observe it is judged as enforce — only an on-chain settler's echo
767
+ // settles. Formal receipt headers keep observing; settlementAuth 'off' still judges nothing.
768
+ const echoed = taskLedger.resolveEcho(message, judge !== undefined && !judge.enforce ? { ...judge, enforce: true } : judge);
766
769
  if (echoed.length > 0) {
767
770
  logger.info(`a2a: ${String(echoed.length)} pending task(s) settled by content echo (${echoed.join(', ')})`);
768
771
  }
@@ -3814,7 +3817,8 @@ export function apply(ctx, config) {
3814
3817
  async function dispatchAnywhere(team, message, callerSession, contextId, signal, asyncMode, taskId, peerCaller) {
3815
3818
  // A local online household is an owner-established fact and receives locally.
3816
3819
  // Otherwise only the pinned home may receive; never fall back to directory claims.
3817
- if (CLI_HANDLE_RE.test(team) && parseReceipt(message) !== null && cliDeposits.get(team) === undefined) {
3820
+ const cliReceipt = CLI_HANDLE_RE.test(team) && parseReceipt(message) !== null;
3821
+ if (cliReceipt && cliDeposits.get(team) === undefined) {
3818
3822
  const home = canvasStore.cliHomeOf(team);
3819
3823
  if (home === undefined)
3820
3824
  throw new Error(`CLI receipt home is not pinned for ${team}`);
@@ -3832,6 +3836,8 @@ export function apply(ctx, config) {
3832
3836
  throw new Error(local.error);
3833
3837
  return local;
3834
3838
  }
3839
+ if (cliReceipt)
3840
+ throw new Error(`local CLI household expired before delivery to ${team}; directory fallback refused`);
3835
3841
  const failures = [];
3836
3842
  const candidates = await directoryPeerCandidates(memoizedCardFetch(), team, failures);
3837
3843
  if (candidates.length === 0) {
@@ -0,0 +1,27 @@
1
+ /** Add structured fields to any JSON response exit and recompute its Content-Length. */
2
+ export function withResponseFields(res, fields) {
3
+ const writeHead = res.writeHead.bind(res);
4
+ const end = res.end.bind(res);
5
+ let head;
6
+ res.writeHead = (status, headers) => {
7
+ head = { status, headers: { ...(headers ?? {}) } };
8
+ return res;
9
+ };
10
+ res.end = (body) => {
11
+ let out = body;
12
+ if (typeof body === 'string') {
13
+ try {
14
+ const parsed = JSON.parse(body);
15
+ if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed))
16
+ out = JSON.stringify({ ...parsed, ...fields });
17
+ }
18
+ catch { /* not JSON: unchanged */ }
19
+ }
20
+ if (head !== undefined) {
21
+ if (out !== undefined && 'Content-Length' in head.headers)
22
+ head.headers['Content-Length'] = Buffer.byteLength(out);
23
+ writeHead(head.status, head.headers);
24
+ }
25
+ return end(out);
26
+ };
27
+ }
@@ -42,8 +42,14 @@ model tools.
42
42
  405 with an empty body. The fix is a host upgrade, not a retry.
43
43
  - `a2a-net revoke` deregisters early.
44
44
 
45
+ ## Tasks: ledger and host observability
46
+
47
+ `a2a-net tasks --json` returns the existing `pending`, `dead`, and `archived` arrays plus `settlement` and `conservation` copied unchanged from `/__dsh_a2a/state`. These blocks include the lifetime `settlement.refusalsTotal`, refusal ring and mode, and the host conservation verdict/counters/duplicate ids. Older hosts omit a block they do not expose; the CLI does not manufacture zero counters or a passing verdict. Plain `tasks` appends a conservation line (`conserved`, `delta`) and a settlement line (`mode`, `refusalsTotal`, `offChain`) when the respective block exists.
48
+
45
49
  ## Files (#91 file channel)
46
50
 
51
+ Files do not go through session messages: use the file channel below, never JPFILE-PART/base64 chunks in `route`. Legacy part replies carry `advice.reason: use-file-channel` with the send commands; up to 16 compatible parts are still allowed, while over-limit transfers keep `-32008` / `file-parts-exceeded`. The hint alone does not mean rejection.
52
+
47
53
  - `a2a-net file send <path…> <team> [--note TEXT]` stages each file (≤64 MiB,
48
54
  ≤16 per send) on the home host by sha256, granted to `<team>` for 24h, and
49
55
  routes ONE message with one `[A2A file] v1 sha256=… size=… name=… from=<home>`
@@ -9,6 +9,10 @@
9
9
  - **adopt-style revive only works for previously materialized sessions** —
10
10
  it is an idempotent revive, not a creation path.
11
11
 
12
+ ## Files
13
+
14
+ - **Files do not go through session messages.** Use `a2a_file_send { paths, team }` (up to 16 files, 64 MiB each), then `a2a_file_fetch { ref }` on the received `[A2A file] v1 …` reference. CLI: `a2a-net file send <path…> <team>` and `a2a-net file fetch "<reference line>"`. Do not base64/chunk files into JPFILE-PART messages. During compatibility rollout, accepted legacy parts carry `advice.reason: use-file-channel`; over-limit parts still fail with `-32008` / `file-parts-exceeded` and the same advice. Do not resend rejected parts in a loop.
15
+
12
16
  ## Routing
13
17
 
14
18
  - **Do not blindly re-dispatch after a sync timeout.** -32005 means the
@@ -1,5 +1,9 @@
1
1
  # Routing deep-dive
2
2
 
3
+ ## Files use the file channel
4
+
5
+ Files do not go through session messages. Send with `a2a_file_send { paths, team }` (up to 16 files, 64 MiB each), not `a2a_route` with file bytes or JPFILE-PART chunks. The receiver uses `a2a_file_fetch { ref }` with the `[A2A file] v1 …` line. CLI: `a2a-net file send <path…> <team>` / `a2a-net file fetch "<reference line>"`. One reference message carries the transfer; ordinary routing and receipt rules below apply to that message. Legacy parts still allowed by the transitional cap receive `advice.reason: use-file-channel`; this hint is not a refusal or a completion receipt.
6
+
3
7
  ## The receipt contract
4
8
 
5
9
  Every async route (`wait:false`) registers the task on the peer and returns
@@ -24,6 +24,8 @@ export interface CanvasSnapshot {
24
24
  }>;
25
25
  /** Pinned CLI handle -> linked home origin (only while some canvas team holds the handle). */
26
26
  readonly cliHome?: Readonly<Record<string, string>>;
27
+ /** Local publish cleared an older pin; kept for the owner inspector until membership is removed or re-pinned. */
28
+ readonly cliHomeCleared?: readonly string[];
27
29
  }
28
30
  /** Upper bounds: a canvas taxonomy, not a database. */
29
31
  export declare const CANVAS_TEAM_CAP = 64;
@@ -53,6 +55,7 @@ export declare class CanvasStore {
53
55
  /** Team entries in creation order; members in add order (routing priority). */
54
56
  private teams;
55
57
  private cliHome;
58
+ private cliHomeCleared;
56
59
  private readonly path;
57
60
  private readonly listeners;
58
61
  /**
@@ -80,6 +83,8 @@ export declare class CanvasStore {
80
83
  /** Frozen, operator-linked CLI home; undefined means unknown or ambiguous. */
81
84
  cliHomeOf(handle: string): string | undefined;
82
85
  pinnedCliHomes(): ReadonlyArray<readonly [string, string]>;
86
+ wasCliHomeCleared(handle: string): boolean;
87
+ clearCliHomeForLocal(handle: string): boolean;
83
88
  /**
84
89
  * The named team's remote member refs (zone-qualified team handles) in
85
90
  * routing-priority order — the failover tier after the local live/cold
@@ -6,6 +6,11 @@
6
6
  */
7
7
  import type { ServerResponse } from 'node:http';
8
8
  import type { IdempotencyStore } from './idempotency-store.ts';
9
+ export declare const FILE_CHANNEL_ADVICE: {
10
+ readonly reason: "use-file-channel";
11
+ readonly message: "Files must use the file channel, not session messages: a2a_file_send { paths, team }; CLI: a2a-net file send <path…> <team>.";
12
+ };
13
+ export declare const fileChannelAdvice: (message: string) => typeof FILE_CHANNEL_ADVICE | undefined;
9
14
  export declare const WIRE_ERROR_RATE_LIMITED = -32008;
10
15
  export declare const MAX_PARTS_PER_TRANSFER = 16;
11
16
  export declare const DEFAULT_RATE_PER_MINUTE = 16;
@@ -0,0 +1,3 @@
1
+ import type { ServerResponse } from 'node:http';
2
+ /** Add structured fields to any JSON response exit and recompute its Content-Length. */
3
+ export declare function withResponseFields(res: ServerResponse, fields: Readonly<Record<string, unknown>>): void;
@@ -134,6 +134,7 @@ export interface CanvasMemberRow {
134
134
  /** Present only for CLI refs: null means the linked home is ambiguous or unknown. */
135
135
  readonly cliHome?: string | null
136
136
  readonly cliHeld?: boolean
137
+ readonly cliHomeCleared?: boolean
137
138
  }
138
139
 
139
140
  /** One canvas team from /state `canvas.teams`. */
@@ -984,6 +985,11 @@ export function createWorkbench(deps: WorkbenchDeps): Workbench {
984
985
  })
985
986
  row.appendChild(remove)
986
987
  card.appendChild(row)
988
+ if (member.cliHomeCleared === true) {
989
+ const notice = el('div', 'wb-hint')
990
+ notice.textContent = '本机落户已清除旧 home 钉;户口离线后也不会恢复旧钉。'
991
+ card.appendChild(notice)
992
+ }
987
993
  if (member.cliHome === null && member.cliHeld !== true && deps.cliHomeOptions !== undefined) {
988
994
  const key = JSON.stringify([fresh.name, member.id])
989
995
  let form = cliHomeForms.get(key)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nelsonlongxiang/dsh-open-a2a-net",
3
- "version": "0.6.34",
3
+ "version": "0.6.35",
4
4
  "description": "Open A2A network plugin for DeepSeek Harness: signed agent cards, decentralized peer/zone discovery, direct routing model tools, and joinable session nodes with a sidebar control",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",