@nelsonlongxiang/dsh-open-a2a-net 0.6.42 → 0.7.0

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 (79) hide show
  1. package/README.md +1 -0
  2. package/README_zh.md +1 -0
  3. package/assets/nexusDist/assets/index-CA_YZTWG.js +9 -0
  4. package/assets/nexusDist/index.html +1 -1
  5. package/cli/a2a.mjs +43 -7
  6. package/lib/a2a-client.js +10 -2
  7. package/lib/admitted-bookkeeping.js +7 -0
  8. package/lib/canvas-discovery.js +11 -1
  9. package/lib/canvas-member.js +43 -0
  10. package/lib/canvas-store.js +315 -17
  11. package/lib/client.js +62 -72
  12. package/lib/client.js.map +1 -1
  13. package/lib/config.js +1 -0
  14. package/lib/direct-forward.js +9 -7
  15. package/lib/faces/admin.js +150 -67
  16. package/lib/faces/files.js +2 -1
  17. package/lib/faces/wire.js +569 -469
  18. package/lib/followup-delivery.js +89 -0
  19. package/lib/idempotency-store.js +8 -0
  20. package/lib/inbound-verification.js +23 -0
  21. package/lib/index.js +534 -166
  22. package/lib/nudge-observability.js +34 -0
  23. package/lib/origin-claim.js +36 -9
  24. package/lib/outbound-scope.js +4 -2
  25. package/lib/peer-directory.js +15 -5
  26. package/lib/peer-store.js +31 -17
  27. package/lib/queue-guard.js +4 -3
  28. package/lib/receipt-ladder.js +1 -1
  29. package/lib/receipt-retry.js +58 -0
  30. package/lib/reconciliation-reminders.js +148 -0
  31. package/lib/reconciliation-state.js +27 -0
  32. package/lib/session-transcript.js +46 -18
  33. package/lib/skill/SKILL.md +5 -0
  34. package/lib/skill/references/cli.md +4 -1
  35. package/lib/skill/references/routing.md +31 -1
  36. package/lib/task-auth.js +3 -3
  37. package/lib/task-delivery.js +23 -0
  38. package/lib/task-ledger-persistence.js +51 -0
  39. package/lib/task-ledger-rows.js +5 -0
  40. package/lib/task-ledger.js +219 -20
  41. package/lib/tools/info.js +37 -7
  42. package/lib/types/a2a-client.d.ts +1 -1
  43. package/lib/types/admitted-bookkeeping.d.ts +5 -0
  44. package/lib/types/canvas-member.d.ts +10 -0
  45. package/lib/types/canvas-store.d.ts +56 -1
  46. package/lib/types/config.d.ts +2 -0
  47. package/lib/types/faces/admin.d.ts +21 -0
  48. package/lib/types/faces/files.d.ts +1 -1
  49. package/lib/types/faces/wire.d.ts +21 -14
  50. package/lib/types/followup-delivery.d.ts +25 -0
  51. package/lib/types/idempotency-store.d.ts +2 -0
  52. package/lib/types/inbound-verification.d.ts +14 -0
  53. package/lib/types/index.d.ts +5 -0
  54. package/lib/types/nudge-observability.d.ts +17 -0
  55. package/lib/types/origin-claim.d.ts +7 -1
  56. package/lib/types/outbound-scope.d.ts +1 -0
  57. package/lib/types/peer-store.d.ts +4 -0
  58. package/lib/types/queue-guard.d.ts +9 -2
  59. package/lib/types/receipt-ladder.d.ts +2 -0
  60. package/lib/types/receipt-retry.d.ts +22 -0
  61. package/lib/types/reconciliation-reminders.d.ts +41 -0
  62. package/lib/types/reconciliation-state.d.ts +32 -0
  63. package/lib/types/session-transcript.d.ts +6 -1
  64. package/lib/types/task-auth.d.ts +7 -0
  65. package/lib/types/task-delivery.d.ts +17 -0
  66. package/lib/types/task-ledger-persistence.d.ts +3 -0
  67. package/lib/types/task-ledger-rows.d.ts +19 -2
  68. package/lib/types/task-ledger.d.ts +48 -1
  69. package/lib/types/tools/info.d.ts +3 -0
  70. package/lib/types/types.d.ts +3 -0
  71. package/nexus-stage/src/federation.ts +1 -0
  72. package/nexus-stage/src/main.ts +17 -2
  73. package/nexus-stage/src/member-provenance-picker.ts +13 -0
  74. package/nexus-stage/src/planning-member-handles.ts +4 -1
  75. package/nexus-stage/src/planning-view.ts +3 -3
  76. package/nexus-stage/src/workbench.ts +51 -7
  77. package/nexus-stage/src/world.ts +1 -0
  78. package/package.json +3 -2
  79. package/assets/nexusDist/assets/index-NkNSeIx6.js +0 -9
@@ -0,0 +1,89 @@
1
+ import { SessionId } from '@deepseek-ai/dsh-session';
2
+ import { MessageId } from '@deepseek-ai/dsh-llm';
3
+ import { AdmittedBookkeepingError } from "./admitted-bookkeeping.js";
4
+ import { deliveryFingerprint } from "./task-delivery.js";
5
+ import { createHash } from 'node:crypto';
6
+ export class FollowupDelivery {
7
+ d;
8
+ constructor(d) {
9
+ this.d = d;
10
+ }
11
+ deliver(agent, text, sender, localFrom, taskId, callerKey, taskExpected = false) {
12
+ // Missing capability is a refusal, never a reason to call steer.
13
+ if (typeof agent.followup !== 'function')
14
+ throw Error('A2A requires Agent.followup; target runtime lacks sourced next-turn delivery');
15
+ const sessionId = String(agent.id), fingerprint = deliveryFingerprint(sessionId, text, sender, localFrom);
16
+ const messageId = MessageId('a2a-followup-' + createHash('sha256').update(taskId === undefined ? fingerprint + ':' + Date.now() + ':' + Math.random() : sessionId + ':' + taskId).digest('hex'));
17
+ if (taskId) {
18
+ const existing = this.d.ledger.deliveryFor(taskId);
19
+ if (existing) {
20
+ if (existing.sessionId !== sessionId || existing.fingerprint !== fingerprint || existing.callerKey !== callerKey)
21
+ throw Error('task delivery identity conflict');
22
+ if (existing.state === 'cancelled' || existing.state === 'failed')
23
+ throw Error('task delivery correspondence is terminal; no replay');
24
+ if (existing.state === 'reserved')
25
+ throw Error('task delivery acceptance uncertain; reconcile the existing MessageId rather than redeliver');
26
+ if (this.d.ledger.orphanDelivery(taskId))
27
+ throw new AdmittedBookkeepingError(undefined);
28
+ return;
29
+ }
30
+ this.d.ledger.reserveDelivery({ version: 1, taskId, messageId, sessionId, fingerprint, ...(taskExpected ? { taskExpected: true } : {}), ...(callerKey ? { callerKey } : {}), createdAt: Date.now(), state: 'reserved' });
31
+ }
32
+ const source = { kind: 'a2a-relay', form: 'relay', ...(sender === undefined ? {} : { senderSessionId: sender }), ...(localFrom === undefined ? {} : { localSender: localFrom }), ...(taskId === undefined ? {} : { taskId }) };
33
+ const message = { id: messageId, role: 'user', content: [{ type: 'text', text }], source };
34
+ this.d.taint(agent, localFrom);
35
+ try {
36
+ agent.followup(message);
37
+ }
38
+ catch (error) {
39
+ // The host may throw after durable insertion; preserve reserved/unknown, never blind replay.
40
+ this.d.warn('FOLLOWUP_ADMISSION_FAILED');
41
+ throw error;
42
+ }
43
+ if (taskId)
44
+ try {
45
+ if (this.d.ledger.deliveryFor(taskId)?.state === 'reserved')
46
+ this.d.ledger.updateDelivery(taskId, 'enqueued');
47
+ }
48
+ catch {
49
+ this.d.warn('FOLLOWUP_ACCEPTED_COMMIT_FAILED');
50
+ }
51
+ }
52
+ /** Only called after the existing cancel authorization has identified the original task. */
53
+ async withdraw(taskId, controller, resolve) {
54
+ const record = this.d.ledger.deliveryFor(taskId);
55
+ if (!record)
56
+ return 'legacy';
57
+ if (record.state === 'cancelled')
58
+ return 'removed';
59
+ if (record.state === 'claimed')
60
+ return 'claimed';
61
+ if (typeof controller?.updateQueue !== 'function')
62
+ throw Error('SessionController.updateQueue unavailable; queued cancellation refused');
63
+ try {
64
+ await controller.updateQueue({ sessionId: SessionId(record.sessionId), itemId: MessageId(record.messageId), action: { kind: 'remove' } });
65
+ this.d.ledger.updateDelivery(taskId, 'cancelled');
66
+ return 'removed';
67
+ }
68
+ catch (error) {
69
+ const code = error?.code;
70
+ if (code === 'session/queue-item-not-found') {
71
+ this.d.ledger.updateDelivery(taskId, 'claimed');
72
+ return 'claimed';
73
+ }
74
+ throw error;
75
+ }
76
+ }
77
+ claimed(agent, messageId) {
78
+ // Called by the owned event boundary; failure must not exit the host.
79
+ try {
80
+ const record = this.d.ledger.deliveryByMessage(String(agent.id), messageId);
81
+ if (record && record.state !== 'cancelled') {
82
+ this.d.ledger.updateDelivery(record.taskId, 'claimed');
83
+ }
84
+ }
85
+ catch {
86
+ this.d.warn('FOLLOWUP_CLAIM_COMMIT_FAILED');
87
+ }
88
+ }
89
+ }
@@ -167,6 +167,14 @@ export class IdempotencyStore {
167
167
  * Unknown or already-settled ids answer `false`. Never throws.
168
168
  * @returns whether the outcome was recorded.
169
169
  */
170
+ /** Release only an unchanged, uncompleted receipt attempt whose settlement did not commit. */
171
+ releaseUncompleted(taskId, fingerprint) {
172
+ const row = this.rows.get(taskId);
173
+ if (row?.fingerprint !== fingerprint || row.outcome !== undefined)
174
+ return;
175
+ this.rows.delete(taskId);
176
+ this.persist();
177
+ }
170
178
  recordOutcome(taskId, outcome, settledAt = this.now()) {
171
179
  if (taskId === '')
172
180
  return false;
@@ -0,0 +1,23 @@
1
+ import { ORIGIN_CLAIM_VERIFY_MS } from "./origin-claim.js";
2
+ export const WIRE_ERROR_INBOUND_VERIFICATION = -32011;
3
+ export function hasOriginClaimFields(req) {
4
+ return Object.keys(req.headers).some(name => name.toLowerCase().startsWith('x-a2a-caller') || name.toLowerCase() === 'x-a2a-body-sha256');
5
+ }
6
+ export async function completeInboundVerification(work, timeoutMs = ORIGIN_CLAIM_VERIFY_MS) {
7
+ let timer;
8
+ const deadline = Date.now() + timeoutMs;
9
+ try {
10
+ const expired = new Promise(resolve => { timer = setTimeout(() => resolve({ ok: false, reason: 'verification-timeout' }), timeoutMs); });
11
+ const result = await Promise.race([Promise.resolve().then(work).catch(() => ({ ok: false, reason: 'no-card' })), expired]);
12
+ return Date.now() >= deadline ? { ok: false, reason: 'verification-timeout' } : result;
13
+ }
14
+ finally {
15
+ if (timer !== undefined)
16
+ clearTimeout(timer);
17
+ }
18
+ }
19
+ export function verifiedInboundAdmission(claim, present, requireBound, admit) {
20
+ if (present && (!claim.ok || (requireBound && !claim.bound)))
21
+ return { ok: false, reason: claim.ok ? 'bound-verification-required' : claim.reason === 'absent' ? 'incomplete-verification' : claim.reason };
22
+ return { ok: true, admission: admit(claim.ok ? claim.card.publicKey : undefined) };
23
+ }