@nelsonlongxiang/dsh-open-a2a-net 0.6.37 → 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.
@@ -70,6 +70,10 @@ export interface AdminFaceDeps extends Pick<CanvasNotifyDeps, 'verifyOriginClaim
70
70
  }>;
71
71
  readonly layoutStore: LayoutStore;
72
72
  readonly taskLedger: TaskLedger;
73
+ readonly enforceRefused?: {
74
+ reason: string;
75
+ blockers: readonly string[];
76
+ };
73
77
  /** #114 S6: observe (lazily) and return the /state `authorization` block. */
74
78
  readonly authorizationView: () => unknown;
75
79
  readonly idempotencyStore: IdempotencyStore;
@@ -140,6 +144,12 @@ export interface AdminFaceDeps extends Pick<CanvasNotifyDeps, 'verifyOriginClaim
140
144
  readonly legacyInboundCount: number;
141
145
  readonly unboundInboundCount: number;
142
146
  readonly boundMissInboundCount: number;
147
+ readonly boundMissRecent: ReadonlyArray<{
148
+ caller: string;
149
+ audience: string;
150
+ count: number;
151
+ lastAt: number;
152
+ }>;
143
153
  readonly ownCardUrl: string;
144
154
  ownerSeatNames: Record<string, string>;
145
155
  ownerAllowlist: string[];
@@ -131,6 +131,7 @@ export interface WireDeps {
131
131
  readonly rawBody?: Uint8Array;
132
132
  }) => Promise<OriginClaimResult>;
133
133
  readonly forwardDirect: (request: ForwardRequest) => Promise<A2aRouteResult>;
134
+ readonly diagnoseCanvas: (team: string, reason: string, caller?: string) => Promise<string>;
134
135
  readonly remoteCliProof: (handle: string, claim: Extract<OriginClaimResult, {
135
136
  ok: true;
136
137
  }>) => Promise<RemoteCliProof | undefined>;
@@ -169,6 +170,7 @@ export interface WireDeps {
169
170
  /** gh#224: refuses a new delivery once the target already owes `targetQueueCap` receipts. */
170
171
  readonly refuseIfQueueFull: (caller: string, message: string, team: string, taskId: string, mailbox: boolean, res: ServerResponse) => boolean;
171
172
  /** Settles a receipt; returns the enforce refusal of the row its header names (#139), if any. */
173
+ readonly waitForForwardBirth: (message: string) => Promise<void>;
172
174
  readonly settleAndAnnounce: (message: string, settler?: Settler) => SettlementRefusal | undefined;
173
175
  readonly touchWakeDemand: () => void;
174
176
  readonly session: string;
@@ -199,7 +201,7 @@ export interface WireDeps {
199
201
  readonly nativeTeamsRound: (prepared: Extract<NativePrepared, {
200
202
  ok: true;
201
203
  }>, team: string, caller: string, message: string, taskId: string | undefined, proofClause: string, external?: AbortSignal, proofHeader?: (team: string, taskId: string, from: string, proofClause: string) => string) => Promise<NativeRoundOutcome>;
202
- readonly routeIntoAgent: (team: string, message: string, caller: string, taskId?: string, proofClause?: string, directHeader?: (from: string) => string) => Promise<RouteOutcome>;
204
+ readonly routeIntoAgent: (team: string, message: string, caller: string, taskId?: string, proofClause?: string, directHeader?: (from: string) => string, localFrom?: string, onSteered?: (target: Agent, floor: number) => void) => Promise<RouteOutcome>;
203
205
  }
204
206
  /**
205
207
  * #187: what a verified claim may vouch for at settlement. The signing key always (the card was
@@ -91,7 +91,7 @@ export interface Config {
91
91
  * (which is left untouched). Inbound faces keep answering. Default false.
92
92
  */
93
93
  readonly isolated: boolean;
94
- /** Forward online local CLI deliveries one hop after a local miss. Default off. */
94
+ /** Forward online local CLI deliveries one hop after a local miss. Default on from 0.6.38; explicit false opts out. */
95
95
  readonly forwardDirect: boolean;
96
96
  /** Human-facing name published on the agent card. */
97
97
  readonly agentName: string;
@@ -117,7 +117,10 @@ export declare function verifyOriginClaim(req: IncomingMessage, d: {
117
117
  /** Count one accepted unbound envelope (the drain gauge). */
118
118
  readonly onUnbound: () => void;
119
119
  /** Count one bound form that missed and fell back to the unbound one. */
120
- readonly onBoundMiss?: () => void;
120
+ readonly onBoundMiss?: (miss: {
121
+ readonly caller: string;
122
+ readonly audience: string;
123
+ }) => void;
121
124
  /** A loopback arrival is an audience of its own (default); off when originClaimUrl declares a tunnel or proxy. */
122
125
  readonly loopbackArrival?: boolean;
123
126
  readonly now: number;
@@ -232,6 +232,8 @@ export interface SettlementRow {
232
232
  readonly peerHost?: string;
233
233
  /** #139: the member that actually received the task (canvas and bare-zone deliveries). */
234
234
  readonly deliveredTo?: string;
235
+ readonly forwarded?: true;
236
+ readonly receiverMembers?: readonly string[];
235
237
  }
236
238
  /** Facts from outside the row. */
237
239
  export interface SettlementFacts {
@@ -96,31 +96,36 @@ export declare function withAuth(raw: unknown, entry: {
96
96
  direction?: unknown;
97
97
  team?: unknown;
98
98
  peer?: unknown;
99
+ forwarded?: unknown;
100
+ sender?: unknown;
99
101
  }, now: number): {
100
102
  auth?: TaskAuthorization;
101
103
  };
102
104
  /** Routing and answer facts copied unchanged between a row and its archive entry. */
103
- export declare function routingOf(row: Pick<TaskRecord, 'peerKey' | 'peerHost' | 'deliveredTo' | 'forwarded' | 'answeredAt'>): Pick<TaskRecord, 'peerKey' | 'peerHost' | 'deliveredTo' | 'forwarded' | 'answeredAt'>;
105
+ export declare function routingOf(row: Pick<TaskRecord, 'peerKey' | 'peerHost' | 'deliveredTo' | 'forwarded' | 'answeredAt' | 'receiverMembers'>): Pick<TaskRecord, 'peerKey' | 'peerHost' | 'deliveredTo' | 'forwarded' | 'answeredAt' | 'receiverMembers'>;
104
106
  /** #139: what an outbound row is born with besides its sender. */
105
107
  export interface OutboundBirth {
106
108
  readonly forwarded?: true;
107
- /** The local-candidate admission (ingress local); kept only on a row whose peer is 'local'. */
109
+ /** Local-candidate admission, or a forwarded online CLI's verified wire admission. */
108
110
  readonly auth?: TaskAuthorization;
109
111
  readonly peerKey?: string;
110
112
  readonly peerHost?: string;
111
113
  readonly deliveredTo?: string;
114
+ /** Verified selected peer canvas membership at dispatch; actual deliveredTo remains stricter when available. */
115
+ readonly receiverMembers?: readonly string[];
112
116
  }
113
117
  /** The facts of one outbound birth that fit the row (see {@link withRouting} for the same shape on restore). */
114
- export declare function birthFacts(peer: string, birth: OutboundBirth | undefined): Pick<TaskRecord, 'auth' | 'peerKey' | 'peerHost' | 'deliveredTo' | 'forwarded'>;
118
+ export declare function birthFacts(peer: string, birth: OutboundBirth | undefined, team?: string, sender?: Sender, now?: number, warn?: (message: string) => void): Pick<TaskRecord, 'auth' | 'peerKey' | 'peerHost' | 'deliveredTo' | 'forwarded' | 'receiverMembers'>;
115
119
  /** Bound on a persisted routing fact; anything longer (or empty, or not a string) is dropped on restore. */
116
120
  export declare const ROUTING_FACT_CAP = 256;
121
+ export declare function parseReceiverMembers(value: unknown, warn?: (message: string) => void): readonly string[] | undefined;
117
122
  /**
118
123
  * #139: restore-side allowlist for the routing facts. Each is kept only in
119
124
  * its shape — a non-empty string within the cap — and the peer facts only on
120
125
  * a remote outbound row; anything else is dropped (the row survives as one
121
126
  * born before the fields, judged by current membership and origin).
122
127
  */
123
- export declare function withRouting(entry: Record<string, unknown>): Pick<TaskRecord, 'peerKey' | 'peerHost' | 'deliveredTo' | 'forwarded' | 'answeredAt'>;
128
+ export declare function withRouting(entry: Record<string, unknown>, warn?: (message: string) => void): Pick<TaskRecord, 'peerKey' | 'peerHost' | 'deliveredTo' | 'forwarded' | 'answeredAt' | 'receiverMembers'>;
124
129
  /** One tracked outbound task still in the owed book (unsettled). */
125
130
  export interface TaskRecord {
126
131
  readonly taskId: string;
@@ -164,6 +169,8 @@ export interface TaskRecord {
164
169
  readonly peerHost?: string;
165
170
  /** #139: the member that actually received the task (canvas / bare-zone deliveries). */
166
171
  readonly deliveredTo?: string;
172
+ /** Verified selected peer canvas membership at dispatch; actual deliveredTo remains stricter when available. */
173
+ readonly receiverMembers?: readonly string[];
167
174
  /** Local target has produced an answer; still owed until receipt delivery/settlement, but no longer queued. */
168
175
  answeredAt?: number;
169
176
  status: TaskStatus;
@@ -221,6 +228,8 @@ export interface ArchivedRecord {
221
228
  readonly peerKey?: string;
222
229
  readonly peerHost?: string;
223
230
  readonly deliveredTo?: string;
231
+ /** Verified selected peer canvas membership at dispatch; actual deliveredTo remains stricter when available. */
232
+ readonly receiverMembers?: readonly string[];
224
233
  /** Local answer time retained as audit metadata, independent of resolution time. */
225
234
  readonly answeredAt?: number;
226
235
  /** #114 S4: who settled the row (receipt and echo ends), and their chain role. */
@@ -252,6 +261,8 @@ export interface CancelledResult extends AbandonResult {
252
261
  }
253
262
  /** Per-instance knobs; defaults come from the exported constants. */
254
263
  export interface TaskLedgerOptions {
264
+ /** Invalid persisted member entries are dropped individually and reported without their content. */
265
+ warn?: (message: string) => void;
255
266
  /** Stale-TTL override (tests inject short windows); defaults to {@link TASK_STALE_TTL_MS}. */
256
267
  staleTtlMs?: number;
257
268
  /** Archive capacity override; defaults to {@link ARCHIVE_CAP}. */
@@ -331,7 +342,7 @@ export declare const SETTLEMENT_REFUSAL_CAP = 64;
331
342
  export interface SettlementJudge {
332
343
  readonly enforce: boolean;
333
344
  readonly settler: SettlementRefusalRecord['settler'];
334
- check(row: Pick<TaskRecord, 'team' | 'peer' | 'direction' | 'auth' | 'peerKey' | 'peerHost' | 'deliveredTo'>, echo: boolean): SettlementVerdict;
345
+ check(row: Pick<TaskRecord, 'team' | 'peer' | 'direction' | 'auth' | 'peerKey' | 'peerHost' | 'deliveredTo' | 'forwarded' | 'receiverMembers'>, echo: boolean): SettlementVerdict;
335
346
  }
336
347
  /** Restore-side allowlist for settledBy: whole or nothing. */
337
348
  export declare function withSettledBy(raw: unknown): {
@@ -44,6 +44,7 @@ export declare class TaskLedger {
44
44
  private readonly archiveCap;
45
45
  /** Injectable clock (option `now`). */
46
46
  private readonly now;
47
+ private readonly warn;
47
48
  /**
48
49
  * @param path - persistence file (`<dsh-home>/a2a/tasks.json`); empty = no persistence.
49
50
  * @param options - TTL/capacity overrides; defaults come from the exported constants.
@@ -55,6 +56,8 @@ export declare class TaskLedger {
55
56
  * @returns the owed-book records.
56
57
  */
57
58
  list(): readonly TaskRecord[];
59
+ /** #275: old forwarded canvas debts cannot be safely upgraded by inventing receiver authority. */
60
+ settlementEnforceBlockers(): string[];
58
61
  /**
59
62
  * The settled archive, most recently resolved first. Rows land here the
60
63
  * moment their receipt correlates and stay until the archive cap evicts
@@ -18,6 +18,9 @@ export type DirectoryTeamRow = {
18
18
  session: string;
19
19
  name: string;
20
20
  seatName?: string;
21
+ kind?: 'canvas';
22
+ owner?: string;
23
+ memberCount?: number;
21
24
  description: string;
22
25
  local?: boolean;
23
26
  origin?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nelsonlongxiang/dsh-open-a2a-net",
3
- "version": "0.6.37",
3
+ "version": "0.6.38",
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",