@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.
- package/README.md +7 -4
- package/README_zh.md +7 -4
- package/assets/nexusDist/assets/index-C7HagaGy.css +1 -0
- package/assets/nexusDist/assets/index-NkNSeIx6.js +9 -0
- package/assets/nexusDist/index.html +2 -2
- package/cli/a2a.mjs +35 -5
- package/lib/a2a-client.js +3 -3
- package/lib/canvas-discovery.js +29 -0
- package/lib/client.js +63 -63
- package/lib/direct-forward.js +49 -27
- package/lib/faces/admin.js +30 -4
- package/lib/faces/canvas-notify.js +8 -6
- package/lib/faces/wire.js +29 -10
- package/lib/index.js +107 -24
- package/lib/origin-claim.js +31 -8
- package/lib/owner-settings.js +38 -1
- package/lib/peer-directory.js +1 -1
- package/lib/peer-link-display.js +15 -0
- package/lib/skill/references/cli.md +11 -3
- package/lib/skill/references/pitfalls.md +1 -1
- package/lib/skill/references/setup.md +9 -9
- package/lib/task-auth.js +1 -1
- package/lib/task-ledger-rows.js +220 -0
- package/lib/task-ledger.js +13 -196
- package/lib/tools/info.js +14 -5
- package/lib/types/a2a-client.d.ts +1 -0
- package/lib/types/canvas-discovery.d.ts +20 -0
- package/lib/types/direct-forward.d.ts +9 -0
- package/lib/types/faces/admin.d.ts +11 -0
- package/lib/types/faces/canvas-notify.d.ts +3 -1
- package/lib/types/faces/wire.d.ts +4 -1
- package/lib/types/index.d.ts +3 -1
- package/lib/types/origin-claim.d.ts +16 -4
- package/lib/types/owner-settings.d.ts +5 -0
- package/lib/types/peer-link-display.d.ts +13 -0
- package/lib/types/task-auth.d.ts +2 -0
- package/lib/types/task-ledger-rows.d.ts +352 -0
- package/lib/types/task-ledger.d.ts +7 -288
- package/lib/types/tools/info.d.ts +5 -0
- package/lib/types/types.d.ts +2 -1
- package/nexus-stage/src/planning-member-handles.ts +28 -1
- package/nexus-stage/src/planning-menus.ts +12 -5
- package/nexus-stage/src/planning-view.ts +18 -14
- package/nexus-stage/src/seat-name-editor.ts +31 -0
- package/nexus-stage/src/session-drag.ts +64 -0
- package/nexus-stage/src/wb-directory.ts +2 -0
- package/nexus-stage/src/workbench.css +2 -0
- package/nexus-stage/src/workbench.ts +16 -5
- package/package.json +1 -1
- package/assets/nexusDist/assets/index-DIQNDBy6.js +0 -9
- 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.
|
|
26
|
-
const css = ".
|
|
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": "
|
|
37
|
-
"activityList": "
|
|
38
|
-
"activityPeer": "
|
|
39
|
-
"activityRow": "
|
|
40
|
-
"activityTeam": "
|
|
41
|
-
"activityTime": "
|
|
42
|
-
"badgePulse": "
|
|
43
|
-
"canvasActions": "
|
|
44
|
-
"canvasChip": "
|
|
45
|
-
"canvasChipRemove": "
|
|
46
|
-
"canvasCreate": "
|
|
47
|
-
"cold": "
|
|
48
|
-
"description": "
|
|
49
|
-
"empty": "
|
|
50
|
-
"facts": "
|
|
51
|
-
"failMark": "
|
|
52
|
-
"flightPulse": "
|
|
53
|
-
"fresh": "
|
|
54
|
-
"freshFlash": "
|
|
55
|
-
"groupButton": "
|
|
56
|
-
"groupChevron": "
|
|
57
|
-
"groupChevronClosed": "
|
|
58
|
-
"groupHead": "
|
|
59
|
-
"groupInput": "
|
|
60
|
-
"groupNew": "
|
|
61
|
-
"groupOption": "
|
|
62
|
-
"groupOptionActive": "
|
|
63
|
-
"groupPicker": "
|
|
64
|
-
"groupTag": "
|
|
65
|
-
"in": "
|
|
66
|
-
"inFlightList": "
|
|
67
|
-
"inFlightPulse": "
|
|
68
|
-
"inFlightRow": "
|
|
69
|
-
"inFlightStale": "
|
|
70
|
-
"joined": "
|
|
71
|
-
"live": "
|
|
72
|
-
"name": "
|
|
73
|
-
"nameRow": "
|
|
74
|
-
"note": "
|
|
75
|
-
"out": "
|
|
76
|
-
"peerChip": "
|
|
77
|
-
"peerGrid": "
|
|
78
|
-
"peerHost": "
|
|
79
|
-
"peerScore": "
|
|
80
|
-
"popover": "
|
|
81
|
-
"row": "
|
|
82
|
-
"searchInput": "
|
|
83
|
-
"sectionTitle": "
|
|
84
|
-
"sessionGroup": "
|
|
85
|
-
"stageLink": "
|
|
86
|
-
"stageLinks": "
|
|
87
|
-
"stageRow": "
|
|
88
|
-
"stageTitle": "
|
|
89
|
-
"staleNote": "
|
|
90
|
-
"stateDot": "
|
|
91
|
-
"team": "
|
|
92
|
-
"title": "
|
|
93
|
-
"trigger": "
|
|
94
|
-
"unreadBadge": "
|
|
95
|
-
"versionTag": "
|
|
96
|
-
"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
|
package/lib/direct-forward.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
35
|
-
const
|
|
36
|
-
d.
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
};
|
package/lib/faces/admin.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
//
|
|
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
|
-
|
|
102
|
-
|
|
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
|
-
|
|
115
|
-
|
|
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(
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
...(
|
|
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',
|