sentinelayer-cli 0.44.0 → 0.46.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.
- package/README.md +10 -5
- package/package.json +1 -1
- package/src/agents/jules/tools/aidenid-email.js +7 -9
- package/src/agents/jules/tools/frontend-analyze.js +15 -3
- package/src/agents/jules/tools/runtime-audit.js +51 -21
- package/src/agents/shared-tools/shell.js +9 -0
- package/src/ai/aidenid.js +5 -4
- package/src/ai/proxy.js +15 -14
- package/src/audit/orchestrator.js +8 -5
- package/src/auth/admission-scope.js +5 -3
- package/src/auth/credential-destinations.js +221 -0
- package/src/auth/gate.js +4 -4
- package/src/auth/http.js +22 -5
- package/src/auth/service.js +52 -56
- package/src/cli.js +24 -6
- package/src/commands/ai/provision-governance.js +7 -6
- package/src/commands/init.js +25 -16
- package/src/commands/scan.js +6 -4
- package/src/commands/session.js +33 -45
- package/src/commands/spec.js +2 -2
- package/src/commands/watch.js +4 -3
- package/src/config/service.js +37 -0
- package/src/daemon/ast-parser-layer.js +8 -3
- package/src/daemon/callgraph-overlay.js +7 -2
- package/src/daemon/pulse.js +2 -1
- package/src/daemon/watchdog.js +117 -68
- package/src/legacy-cli.js +119 -39
- package/src/mcp/bridge-inputs.js +223 -0
- package/src/mcp/cli-command-tools.js +157 -7
- package/src/mcp/registry.js +2 -1
- package/src/mcp/session-stdio-server.js +114 -35
- package/src/mcp/smoke.js +27 -11
- package/src/mcp/token-service.js +7 -9
- package/src/memory/retrieval.js +14 -5
- package/src/pocket/ring-owner.js +13 -8
- package/src/program-lookup.js +54 -0
- package/src/review/ai-review.js +2 -2
- package/src/review/dd-report-email-client.js +4 -5
- package/src/review/local-review.js +11 -5
- package/src/session/admission-access.js +13 -10
- package/src/session/admission-auth.js +95 -16
- package/src/session/admission.js +44 -9
- package/src/session/checkpoints.js +11 -10
- package/src/session/file-locks.js +19 -18
- package/src/session/invitations.js +26 -19
- package/src/session/live-source.js +13 -13
- package/src/session/route-classes.js +107 -0
- package/src/session/sync.js +70 -56
- package/src/session/tickets.js +21 -19
- package/src/session/title-sync.js +4 -3
- package/src/telemetry/sync.js +8 -9
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// route-classes.js -- the ONE classification of every local MCP session tool ("mcp:<tool>")
|
|
2
|
+
// and every `sl session` command the MCP CLI bridge can invoke ("cli:session <path>").
|
|
3
|
+
//
|
|
4
|
+
// In an agent context it decides what a route may do when the session it acts in stores NO
|
|
5
|
+
// admission on this machine (when the session stores one, every route binds to it or is
|
|
6
|
+
// refused; see runInSessionToolContext in src/mcp/session-stdio-server.js and
|
|
7
|
+
// assertDispatchMatchesScope in src/session/admission-auth.js):
|
|
8
|
+
//
|
|
9
|
+
// legacy-data-plane a compatibility list for rooms that don't require admission:
|
|
10
|
+
// posting, replying, reading/polling, message actions and reactions,
|
|
11
|
+
// attention requests. Runs as before; the server's admission mode
|
|
12
|
+
// decides whether a room accepts it.
|
|
13
|
+
// control leases, locks, guards, tickets, checkpoints, joining and leaving,
|
|
14
|
+
// listeners, room setup and everything else that is not plain
|
|
15
|
+
// data-plane traffic. Refused unless the session's admission binds it.
|
|
16
|
+
// owner room owner actions. Refused in an agent context.
|
|
17
|
+
// exempt-local sends no request (a local store or built-in constants only).
|
|
18
|
+
//
|
|
19
|
+
// A route missing from this table is treated as unclassified and refused like control. A
|
|
20
|
+
// unit test derives every MCP tool and every bridge-invokable command from the real handlers
|
|
21
|
+
// and command tree and fails when one is missing here.
|
|
22
|
+
|
|
23
|
+
export const SESSION_ROUTE_CLASSES = Object.freeze({
|
|
24
|
+
// ---- local MCP session tools
|
|
25
|
+
"mcp:poll_inbox": "legacy-data-plane",
|
|
26
|
+
"mcp:read_history": "legacy-data-plane",
|
|
27
|
+
"mcp:send_message": "legacy-data-plane",
|
|
28
|
+
"mcp:attention_request": "legacy-data-plane",
|
|
29
|
+
"mcp:session_action": "legacy-data-plane",
|
|
30
|
+
"mcp:session_react": "legacy-data-plane",
|
|
31
|
+
"mcp:session_reply": "legacy-data-plane",
|
|
32
|
+
"mcp:session_lock": "control",
|
|
33
|
+
"mcp:session_unlock": "control",
|
|
34
|
+
"mcp:session_locks": "control",
|
|
35
|
+
"mcp:memory.write": "exempt-local",
|
|
36
|
+
"mcp:memory.recall": "exempt-local",
|
|
37
|
+
"mcp:memory.summarize": "exempt-local",
|
|
38
|
+
|
|
39
|
+
// ---- `sl session` commands invokable through the MCP CLI bridge
|
|
40
|
+
"cli:session say": "legacy-data-plane",
|
|
41
|
+
"cli:session post-agent": "legacy-data-plane",
|
|
42
|
+
"cli:session observe": "legacy-data-plane",
|
|
43
|
+
"cli:session reply": "legacy-data-plane",
|
|
44
|
+
"cli:session comment": "legacy-data-plane",
|
|
45
|
+
"cli:session action": "legacy-data-plane",
|
|
46
|
+
"cli:session react": "legacy-data-plane",
|
|
47
|
+
"cli:session read": "legacy-data-plane",
|
|
48
|
+
"cli:session history": "legacy-data-plane",
|
|
49
|
+
"cli:session search": "legacy-data-plane",
|
|
50
|
+
"cli:session pins": "legacy-data-plane",
|
|
51
|
+
"cli:session lock": "control",
|
|
52
|
+
"cli:session unlock": "control",
|
|
53
|
+
"cli:session locks": "control",
|
|
54
|
+
"cli:session renew": "control",
|
|
55
|
+
"cli:session guard": "control",
|
|
56
|
+
"cli:session guard-hook": "control",
|
|
57
|
+
"cli:session guard-install": "control",
|
|
58
|
+
"cli:session guard-uninstall": "control",
|
|
59
|
+
"cli:session checkpoint create": "control",
|
|
60
|
+
"cli:session checkpoint generate": "control",
|
|
61
|
+
"cli:session checkpoint list": "control",
|
|
62
|
+
"cli:session checkpoint show": "control",
|
|
63
|
+
"cli:session ticket claim": "control",
|
|
64
|
+
"cli:session ticket events": "control",
|
|
65
|
+
"cli:session ticket list": "control",
|
|
66
|
+
"cli:session ticket release": "control",
|
|
67
|
+
"cli:session ticket renew": "control",
|
|
68
|
+
"cli:session ticket submit": "control",
|
|
69
|
+
"cli:session join": "control",
|
|
70
|
+
"cli:session leave": "control",
|
|
71
|
+
"cli:session stop-listener": "control",
|
|
72
|
+
"cli:session listeners": "control",
|
|
73
|
+
"cli:session set-title": "control",
|
|
74
|
+
"cli:session start": "control",
|
|
75
|
+
"cli:session ensure": "control",
|
|
76
|
+
"cli:session continue": "control",
|
|
77
|
+
"cli:session inject-guide": "control",
|
|
78
|
+
"cli:session setup-guides": "control",
|
|
79
|
+
"cli:session sync": "control",
|
|
80
|
+
"cli:session status": "control",
|
|
81
|
+
"cli:session list": "control",
|
|
82
|
+
"cli:session usage": "control",
|
|
83
|
+
"cli:session recall": "control",
|
|
84
|
+
"cli:session recap now": "control",
|
|
85
|
+
"cli:session edit": "control",
|
|
86
|
+
"cli:session wake codex": "control",
|
|
87
|
+
"cli:session wake codex-notify": "control",
|
|
88
|
+
"cli:session wake daemon": "control",
|
|
89
|
+
"cli:session access approve": "owner",
|
|
90
|
+
"cli:session access deny": "owner",
|
|
91
|
+
"cli:session access revoke": "owner",
|
|
92
|
+
"cli:session access mode": "owner",
|
|
93
|
+
"cli:session access request": "owner",
|
|
94
|
+
"cli:session access list": "owner",
|
|
95
|
+
"cli:session access status": "owner",
|
|
96
|
+
"cli:session actions": "exempt-local",
|
|
97
|
+
"cli:session templates": "exempt-local",
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
/** The compatibility list for rooms that don't require admission, as one named view of the table. */
|
|
101
|
+
export const LEGACY_DATA_PLANE_TOOLS = Object.freeze(
|
|
102
|
+
Object.keys(SESSION_ROUTE_CLASSES).filter((route) => SESSION_ROUTE_CLASSES[route] === "legacy-data-plane"),
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
export function sessionRouteClass(route) {
|
|
106
|
+
return SESSION_ROUTE_CLASSES[route] || "unclassified";
|
|
107
|
+
}
|
package/src/session/sync.js
CHANGED
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import process from "node:process";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
|
6
6
|
|
|
7
|
+
import { credentialedRequest, credentialFor, isAuthenticated } from "../auth/credential-destinations.js";
|
|
7
8
|
import { resolveActiveAuthSession } from "../auth/service.js";
|
|
8
9
|
import { createAgentEvent } from "../events/schema.js";
|
|
9
10
|
import { isSessionControlEvent } from "./control-events.js";
|
|
@@ -378,7 +379,8 @@ function recordCircuitFailure(circuit, nowMs) {
|
|
|
378
379
|
|
|
379
380
|
// 401 and 403 refuse one credential (an expired, revoked or narrower agent admission, or
|
|
380
381
|
// an identity the account has not granted). They say nothing about API health, so they
|
|
381
|
-
// never count toward
|
|
382
|
+
// never count toward either machine-wide breaker (inbound or outbound): one agent's
|
|
383
|
+
// refusals must not pause another agent's traffic. Network errors, timeouts and 5xx do.
|
|
382
384
|
function isCredentialRefusal(response) {
|
|
383
385
|
return Boolean(response) && (response.status === 401 || response.status === 403);
|
|
384
386
|
}
|
|
@@ -388,6 +390,11 @@ function recordOutboundFailure(response, nowMs) {
|
|
|
388
390
|
recordCircuitFailure(outboundCircuit, nowMs);
|
|
389
391
|
}
|
|
390
392
|
|
|
393
|
+
function recordInboundFailure(response, nowMs) {
|
|
394
|
+
if (isCredentialRefusal(response)) return;
|
|
395
|
+
recordCircuitFailure(inboundCircuit, nowMs);
|
|
396
|
+
}
|
|
397
|
+
|
|
391
398
|
function recordCircuitSuccess(circuit) {
|
|
392
399
|
if (!circuit) {
|
|
393
400
|
return;
|
|
@@ -671,13 +678,21 @@ export async function fetchJsonWithFullTimeout(
|
|
|
671
678
|
const controller = new AbortController();
|
|
672
679
|
const startedAtMs = Date.now();
|
|
673
680
|
let response = null;
|
|
681
|
+
// A credential (src/auth/credential-destinations.js) is sent only to its own origin, by
|
|
682
|
+
// credentialedRequest; fetchImpl is the transport that it calls.
|
|
683
|
+
const { credential = null, ...init } = options || {};
|
|
674
684
|
const requestOptions = {
|
|
675
|
-
...
|
|
685
|
+
...init,
|
|
676
686
|
signal: combineAbortSignals([options?.signal, controller.signal]),
|
|
677
687
|
};
|
|
688
|
+
const sent = credential
|
|
689
|
+
? credentialedRequest(credential, url, requestOptions, {
|
|
690
|
+
fetchImpl: (target, request) => fetchImpl(target, request, resolvedTimeoutMs),
|
|
691
|
+
})
|
|
692
|
+
: fetchImpl(url, requestOptions, resolvedTimeoutMs);
|
|
678
693
|
|
|
679
694
|
response = await withTimeout(
|
|
680
|
-
|
|
695
|
+
sent,
|
|
681
696
|
resolvedTimeoutMs,
|
|
682
697
|
{
|
|
683
698
|
reason: "request_timeout",
|
|
@@ -1003,7 +1018,7 @@ export async function syncSessionEventToApi(
|
|
|
1003
1018
|
return { synced: false, reason: "no_session" };
|
|
1004
1019
|
}
|
|
1005
1020
|
|
|
1006
|
-
if (!session
|
|
1021
|
+
if (!isAuthenticated(session)) {
|
|
1007
1022
|
return { synced: false, reason: "not_authenticated" };
|
|
1008
1023
|
}
|
|
1009
1024
|
|
|
@@ -1017,8 +1032,8 @@ export async function syncSessionEventToApi(
|
|
|
1017
1032
|
method: "POST",
|
|
1018
1033
|
headers: {
|
|
1019
1034
|
"Content-Type": "application/json",
|
|
1020
|
-
Authorization: `Bearer ${session.token}`,
|
|
1021
1035
|
},
|
|
1036
|
+
credential: await credentialFor(session),
|
|
1022
1037
|
body: requestBody,
|
|
1023
1038
|
};
|
|
1024
1039
|
const resolvedTimeoutMs = normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS);
|
|
@@ -1078,8 +1093,8 @@ export async function syncSessionEventToApi(
|
|
|
1078
1093
|
method: "POST",
|
|
1079
1094
|
headers: {
|
|
1080
1095
|
"Content-Type": "application/json",
|
|
1081
|
-
Authorization: `Bearer ${session.token}`,
|
|
1082
1096
|
},
|
|
1097
|
+
credential: await credentialFor(session),
|
|
1083
1098
|
body: JSON.stringify({
|
|
1084
1099
|
agent_id: agentId,
|
|
1085
1100
|
role: grantRole,
|
|
@@ -1201,7 +1216,7 @@ async function syncSessionAuxPayload(
|
|
|
1201
1216
|
} catch {
|
|
1202
1217
|
return { synced: false, reason: "no_session" };
|
|
1203
1218
|
}
|
|
1204
|
-
if (!session
|
|
1219
|
+
if (!isAuthenticated(session)) {
|
|
1205
1220
|
return { synced: false, reason: "not_authenticated" };
|
|
1206
1221
|
}
|
|
1207
1222
|
|
|
@@ -1214,8 +1229,8 @@ async function syncSessionAuxPayload(
|
|
|
1214
1229
|
method: "POST",
|
|
1215
1230
|
headers: {
|
|
1216
1231
|
"Content-Type": "application/json",
|
|
1217
|
-
Authorization: `Bearer ${session.token}`,
|
|
1218
1232
|
},
|
|
1233
|
+
credential: await credentialFor(session),
|
|
1219
1234
|
body: JSON.stringify({
|
|
1220
1235
|
...payload,
|
|
1221
1236
|
source: "cli",
|
|
@@ -1320,7 +1335,7 @@ export async function pollHumanMessages(
|
|
|
1320
1335
|
cursor: normalizeString(since) || null,
|
|
1321
1336
|
};
|
|
1322
1337
|
}
|
|
1323
|
-
if (!session
|
|
1338
|
+
if (!isAuthenticated(session)) {
|
|
1324
1339
|
return {
|
|
1325
1340
|
ok: false,
|
|
1326
1341
|
reason: "not_authenticated",
|
|
@@ -1344,14 +1359,14 @@ export async function pollHumanMessages(
|
|
|
1344
1359
|
{
|
|
1345
1360
|
method: "GET",
|
|
1346
1361
|
headers: {
|
|
1347
|
-
Authorization: `Bearer ${session.token}`,
|
|
1348
1362
|
},
|
|
1363
|
+
credential: await credentialFor(session),
|
|
1349
1364
|
},
|
|
1350
1365
|
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
1351
1366
|
fetchImpl,
|
|
1352
1367
|
);
|
|
1353
1368
|
if (!response || !response.ok) {
|
|
1354
|
-
|
|
1369
|
+
recordInboundFailure(response, normalizedNowMs);
|
|
1355
1370
|
return {
|
|
1356
1371
|
ok: false,
|
|
1357
1372
|
reason: `api_${response ? response.status : "no_response"}`,
|
|
@@ -1487,7 +1502,7 @@ export async function pollSessionEvents(
|
|
|
1487
1502
|
cursor: normalizeString(since) || null,
|
|
1488
1503
|
};
|
|
1489
1504
|
}
|
|
1490
|
-
if (!session
|
|
1505
|
+
if (!isAuthenticated(session)) {
|
|
1491
1506
|
return {
|
|
1492
1507
|
ok: false,
|
|
1493
1508
|
reason: "not_authenticated",
|
|
@@ -1518,7 +1533,7 @@ export async function pollSessionEvents(
|
|
|
1518
1533
|
endpoint,
|
|
1519
1534
|
{
|
|
1520
1535
|
method: "GET",
|
|
1521
|
-
|
|
1536
|
+
credential: await credentialFor(session),
|
|
1522
1537
|
},
|
|
1523
1538
|
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
1524
1539
|
fetchImpl,
|
|
@@ -1536,7 +1551,7 @@ export async function pollSessionEvents(
|
|
|
1536
1551
|
cursor: normalizedSince || null,
|
|
1537
1552
|
};
|
|
1538
1553
|
}
|
|
1539
|
-
|
|
1554
|
+
recordInboundFailure(response, normalizedNowMs);
|
|
1540
1555
|
return {
|
|
1541
1556
|
ok: false,
|
|
1542
1557
|
reason: `api_${response ? response.status : "no_response"}`,
|
|
@@ -1655,7 +1670,7 @@ export async function fetchSessionPresence(
|
|
|
1655
1670
|
present: [],
|
|
1656
1671
|
};
|
|
1657
1672
|
}
|
|
1658
|
-
if (!session
|
|
1673
|
+
if (!isAuthenticated(session)) {
|
|
1659
1674
|
return {
|
|
1660
1675
|
ok: false,
|
|
1661
1676
|
reason: "not_authenticated",
|
|
@@ -1672,7 +1687,7 @@ export async function fetchSessionPresence(
|
|
|
1672
1687
|
endpoint,
|
|
1673
1688
|
{
|
|
1674
1689
|
method: "GET",
|
|
1675
|
-
|
|
1690
|
+
credential: await credentialFor(session),
|
|
1676
1691
|
},
|
|
1677
1692
|
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
1678
1693
|
fetchImpl,
|
|
@@ -1698,7 +1713,7 @@ export async function fetchSessionPresence(
|
|
|
1698
1713
|
present: [],
|
|
1699
1714
|
};
|
|
1700
1715
|
}
|
|
1701
|
-
|
|
1716
|
+
recordInboundFailure(response, normalizedNowMs);
|
|
1702
1717
|
return {
|
|
1703
1718
|
ok: false,
|
|
1704
1719
|
reason: `api_${response ? response.status : "no_response"}`,
|
|
@@ -1807,7 +1822,7 @@ export async function renewSessionPresence(
|
|
|
1807
1822
|
recorded: false,
|
|
1808
1823
|
};
|
|
1809
1824
|
}
|
|
1810
|
-
if (!session
|
|
1825
|
+
if (!isAuthenticated(session)) {
|
|
1811
1826
|
return {
|
|
1812
1827
|
ok: false,
|
|
1813
1828
|
reason: "not_authenticated",
|
|
@@ -1836,8 +1851,8 @@ export async function renewSessionPresence(
|
|
|
1836
1851
|
method: "PUT",
|
|
1837
1852
|
headers: {
|
|
1838
1853
|
"Content-Type": "application/json",
|
|
1839
|
-
Authorization: `Bearer ${session.token}`,
|
|
1840
1854
|
},
|
|
1855
|
+
credential: await credentialFor(session),
|
|
1841
1856
|
body: JSON.stringify(body),
|
|
1842
1857
|
signal,
|
|
1843
1858
|
},
|
|
@@ -1932,7 +1947,7 @@ export async function requestSessionListenerStop(
|
|
|
1932
1947
|
} catch {
|
|
1933
1948
|
return { ok: false, reason: "no_session", recorded: false };
|
|
1934
1949
|
}
|
|
1935
|
-
if (!session
|
|
1950
|
+
if (!isAuthenticated(session)) {
|
|
1936
1951
|
return { ok: false, reason: "not_authenticated", recorded: false };
|
|
1937
1952
|
}
|
|
1938
1953
|
|
|
@@ -1952,9 +1967,9 @@ export async function requestSessionListenerStop(
|
|
|
1952
1967
|
method: "POST",
|
|
1953
1968
|
headers: {
|
|
1954
1969
|
"Content-Type": "application/json",
|
|
1955
|
-
Authorization: `Bearer ${session.token}`,
|
|
1956
1970
|
"Idempotency-Key": normalizedIdempotencyKey,
|
|
1957
1971
|
},
|
|
1972
|
+
credential: await credentialFor(session),
|
|
1958
1973
|
body: JSON.stringify(body),
|
|
1959
1974
|
},
|
|
1960
1975
|
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
@@ -2060,7 +2075,7 @@ export async function updateSessionReadCursor(
|
|
|
2060
2075
|
} catch {
|
|
2061
2076
|
return { ok: false, reason: "no_session", updated: false };
|
|
2062
2077
|
}
|
|
2063
|
-
if (!session
|
|
2078
|
+
if (!isAuthenticated(session)) {
|
|
2064
2079
|
return { ok: false, reason: "not_authenticated", updated: false };
|
|
2065
2080
|
}
|
|
2066
2081
|
|
|
@@ -2081,8 +2096,8 @@ export async function updateSessionReadCursor(
|
|
|
2081
2096
|
method: "PUT",
|
|
2082
2097
|
headers: {
|
|
2083
2098
|
"Content-Type": "application/json",
|
|
2084
|
-
Authorization: `Bearer ${session.token}`,
|
|
2085
2099
|
},
|
|
2100
|
+
credential: await credentialFor(session),
|
|
2086
2101
|
body: JSON.stringify(body),
|
|
2087
2102
|
signal,
|
|
2088
2103
|
},
|
|
@@ -2194,7 +2209,7 @@ export async function streamSessionEvents(
|
|
|
2194
2209
|
errorCount: 0,
|
|
2195
2210
|
};
|
|
2196
2211
|
}
|
|
2197
|
-
if (!session
|
|
2212
|
+
if (!isAuthenticated(session)) {
|
|
2198
2213
|
return {
|
|
2199
2214
|
ok: false,
|
|
2200
2215
|
reason: "not_authenticated",
|
|
@@ -2244,18 +2259,18 @@ export async function streamSessionEvents(
|
|
|
2244
2259
|
|
|
2245
2260
|
let response;
|
|
2246
2261
|
try {
|
|
2247
|
-
response = await
|
|
2262
|
+
response = await credentialedRequest(
|
|
2263
|
+
await credentialFor(session),
|
|
2248
2264
|
endpoint,
|
|
2249
2265
|
{
|
|
2250
2266
|
method: "GET",
|
|
2251
2267
|
headers: {
|
|
2252
2268
|
Accept: "text/event-stream",
|
|
2253
|
-
Authorization: `Bearer ${session.token}`,
|
|
2254
2269
|
...(normalizedSince ? { "Last-Event-ID": normalizedSince } : {}),
|
|
2255
2270
|
},
|
|
2256
2271
|
signal: controller.signal,
|
|
2257
2272
|
},
|
|
2258
|
-
normalizedTimeoutMs
|
|
2273
|
+
{ fetchImpl: (target, init) => fetchImpl(target, init, normalizedTimeoutMs) },
|
|
2259
2274
|
);
|
|
2260
2275
|
} catch (error) {
|
|
2261
2276
|
clearTimeout(timeoutHandle);
|
|
@@ -2415,7 +2430,7 @@ export async function pollSessionEventsBefore(
|
|
|
2415
2430
|
beforeSequence: null,
|
|
2416
2431
|
};
|
|
2417
2432
|
}
|
|
2418
|
-
if (!session
|
|
2433
|
+
if (!isAuthenticated(session)) {
|
|
2419
2434
|
return {
|
|
2420
2435
|
ok: false,
|
|
2421
2436
|
reason: "not_authenticated",
|
|
@@ -2442,7 +2457,7 @@ export async function pollSessionEventsBefore(
|
|
|
2442
2457
|
endpoint,
|
|
2443
2458
|
{
|
|
2444
2459
|
method: "GET",
|
|
2445
|
-
|
|
2460
|
+
credential: await credentialFor(session),
|
|
2446
2461
|
},
|
|
2447
2462
|
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
2448
2463
|
fetchImpl,
|
|
@@ -2460,7 +2475,7 @@ export async function pollSessionEventsBefore(
|
|
|
2460
2475
|
beforeSequence: Number.isFinite(normalizedBeforeSequence) ? normalizedBeforeSequence : null,
|
|
2461
2476
|
};
|
|
2462
2477
|
}
|
|
2463
|
-
|
|
2478
|
+
recordInboundFailure(response, normalizedNowMs);
|
|
2464
2479
|
return {
|
|
2465
2480
|
ok: false,
|
|
2466
2481
|
reason: `api_${response ? response.status : "no_response"}`,
|
|
@@ -2538,7 +2553,7 @@ export async function listSessionMessageActions(
|
|
|
2538
2553
|
} catch {
|
|
2539
2554
|
return { ok: false, reason: "no_session", actions: [], count: 0, projection: null };
|
|
2540
2555
|
}
|
|
2541
|
-
if (!session
|
|
2556
|
+
if (!isAuthenticated(session)) {
|
|
2542
2557
|
return { ok: false, reason: "not_authenticated", actions: [], count: 0, projection: null };
|
|
2543
2558
|
}
|
|
2544
2559
|
|
|
@@ -2563,13 +2578,13 @@ export async function listSessionMessageActions(
|
|
|
2563
2578
|
endpoint,
|
|
2564
2579
|
{
|
|
2565
2580
|
method: "GET",
|
|
2566
|
-
|
|
2581
|
+
credential: await credentialFor(session),
|
|
2567
2582
|
},
|
|
2568
2583
|
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
2569
2584
|
fetchImpl,
|
|
2570
2585
|
);
|
|
2571
2586
|
if (!response || !response.ok) {
|
|
2572
|
-
|
|
2587
|
+
recordInboundFailure(response, normalizedNowMs);
|
|
2573
2588
|
return {
|
|
2574
2589
|
ok: false,
|
|
2575
2590
|
reason: `api_${response ? response.status : "no_response"}`,
|
|
@@ -2643,7 +2658,7 @@ export async function fetchSessionUsageLedger(
|
|
|
2643
2658
|
} catch {
|
|
2644
2659
|
return { ok: false, reason: "no_session", status: 0, payload: null };
|
|
2645
2660
|
}
|
|
2646
|
-
if (!session
|
|
2661
|
+
if (!isAuthenticated(session)) {
|
|
2647
2662
|
return { ok: false, reason: "not_authenticated", status: 0, payload: null };
|
|
2648
2663
|
}
|
|
2649
2664
|
|
|
@@ -2660,13 +2675,13 @@ export async function fetchSessionUsageLedger(
|
|
|
2660
2675
|
endpoint,
|
|
2661
2676
|
{
|
|
2662
2677
|
method: "GET",
|
|
2663
|
-
|
|
2678
|
+
credential: await credentialFor(session),
|
|
2664
2679
|
},
|
|
2665
2680
|
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
2666
2681
|
fetchImpl,
|
|
2667
2682
|
);
|
|
2668
2683
|
if (!response || !response.ok) {
|
|
2669
|
-
|
|
2684
|
+
recordInboundFailure(response, normalizedNowMs);
|
|
2670
2685
|
return {
|
|
2671
2686
|
ok: false,
|
|
2672
2687
|
reason: `api_${response ? response.status : "no_response"}`,
|
|
@@ -2842,9 +2857,9 @@ export async function editSessionMessage(sessionId, {
|
|
|
2842
2857
|
let auth;
|
|
2843
2858
|
try { auth = await resolveAuthSession({ cwd: targetPath, env: process.env, autoRotate: false }); }
|
|
2844
2859
|
catch { return { ok: false, reason: "no_session" }; }
|
|
2845
|
-
if (!auth
|
|
2860
|
+
if (!isAuthenticated(auth)) return { ok: false, reason: "not_authenticated" };
|
|
2846
2861
|
const base = `${resolveApiBaseUrl(auth)}/api/v1/sessions/${encodeURIComponent(sid)}`;
|
|
2847
|
-
const
|
|
2862
|
+
const credential = await credentialFor(auth);
|
|
2848
2863
|
const failure = (response, payload) => {
|
|
2849
2864
|
const code = normalizeString(payload?.detail?.code || payload?.error?.code || payload?.code);
|
|
2850
2865
|
// Client/schema/ownership conflicts are terminal and must not poison unrelated writes.
|
|
@@ -2857,7 +2872,7 @@ export async function editSessionMessage(sessionId, {
|
|
|
2857
2872
|
const resource = replyId ? `replies/${encodeURIComponent(replyId)}` : messageId
|
|
2858
2873
|
? `messages/${encodeURIComponent(messageId)}` : `messages/by-sequence/${sequence}`;
|
|
2859
2874
|
const authorQuery = normalizeString(agentId) ? `?agentId=${encodeURIComponent(normalizeString(agentId))}` : "";
|
|
2860
|
-
const { response, payload } = await fetchJsonWithFullTimeout(`${base}/${resource}${authorQuery}`, { method: "GET",
|
|
2875
|
+
const { response, payload } = await fetchJsonWithFullTimeout(`${base}/${resource}${authorQuery}`, { method: "GET", credential, signal }, timeoutMs, fetchImpl, { readErrorBody: true });
|
|
2861
2876
|
if (!response?.ok || payload?.ok === false) return failure(response, payload);
|
|
2862
2877
|
const current = replyId ? payload?.reply : payload?.event;
|
|
2863
2878
|
const fetchedRevision = Number(current?.messageRevision ?? 1);
|
|
@@ -2871,11 +2886,12 @@ export async function editSessionMessage(sessionId, {
|
|
|
2871
2886
|
const { response, payload } = await fetchJsonWithFullTimeout(`${base}/${resource}`, {
|
|
2872
2887
|
method: "PATCH",
|
|
2873
2888
|
headers: {
|
|
2874
|
-
...createSessionMutationHeaders({
|
|
2889
|
+
...createSessionMutationHeaders({ credential, sessionId: sid, routeId: replyId
|
|
2875
2890
|
? "PATCH /api/v1/sessions/{session_id}/replies/{action_id}"
|
|
2876
2891
|
: "PATCH /api/v1/sessions/{session_id}/messages/{message_id}", idempotencyKey: mutationKey }),
|
|
2877
2892
|
"Content-Type": "application/json", "Idempotency-Key": mutationKey,
|
|
2878
2893
|
},
|
|
2894
|
+
credential,
|
|
2879
2895
|
body: JSON.stringify(body),
|
|
2880
2896
|
signal,
|
|
2881
2897
|
}, timeoutMs, fetchImpl, { readErrorBody: true });
|
|
@@ -2964,7 +2980,7 @@ export async function createSessionMessageAction(
|
|
|
2964
2980
|
} catch {
|
|
2965
2981
|
return { ok: false, reason: "no_session", action: null };
|
|
2966
2982
|
}
|
|
2967
|
-
if (!session
|
|
2983
|
+
if (!isAuthenticated(session)) {
|
|
2968
2984
|
return { ok: false, reason: "not_authenticated", action: null };
|
|
2969
2985
|
}
|
|
2970
2986
|
|
|
@@ -3002,8 +3018,8 @@ export async function createSessionMessageAction(
|
|
|
3002
3018
|
method: "POST",
|
|
3003
3019
|
headers: {
|
|
3004
3020
|
"Content-Type": "application/json",
|
|
3005
|
-
Authorization: `Bearer ${session.token}`,
|
|
3006
3021
|
},
|
|
3022
|
+
credential: await credentialFor(session),
|
|
3007
3023
|
body: JSON.stringify(body),
|
|
3008
3024
|
signal,
|
|
3009
3025
|
},
|
|
@@ -3104,7 +3120,7 @@ export async function searchSessionEvents(
|
|
|
3104
3120
|
nextBeforeSequence: null,
|
|
3105
3121
|
};
|
|
3106
3122
|
}
|
|
3107
|
-
if (!session
|
|
3123
|
+
if (!isAuthenticated(session)) {
|
|
3108
3124
|
return {
|
|
3109
3125
|
ok: false,
|
|
3110
3126
|
reason: "not_authenticated",
|
|
@@ -3134,13 +3150,13 @@ export async function searchSessionEvents(
|
|
|
3134
3150
|
endpoint,
|
|
3135
3151
|
{
|
|
3136
3152
|
method: "GET",
|
|
3137
|
-
|
|
3153
|
+
credential: await credentialFor(session),
|
|
3138
3154
|
},
|
|
3139
3155
|
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
3140
3156
|
fetchImpl,
|
|
3141
3157
|
);
|
|
3142
3158
|
if (!response || !response.ok) {
|
|
3143
|
-
|
|
3159
|
+
recordInboundFailure(response, normalizedNowMs);
|
|
3144
3160
|
return {
|
|
3145
3161
|
ok: false,
|
|
3146
3162
|
reason: `api_${response ? response.status : "no_response"}`,
|
|
@@ -3226,7 +3242,7 @@ export async function listSessionsFromApi({
|
|
|
3226
3242
|
warnings: [],
|
|
3227
3243
|
};
|
|
3228
3244
|
}
|
|
3229
|
-
if (!session
|
|
3245
|
+
if (!isAuthenticated(session)) {
|
|
3230
3246
|
return {
|
|
3231
3247
|
ok: false,
|
|
3232
3248
|
reason: "not_authenticated",
|
|
@@ -3262,7 +3278,7 @@ export async function listSessionsFromApi({
|
|
|
3262
3278
|
endpoint,
|
|
3263
3279
|
{
|
|
3264
3280
|
method: "GET",
|
|
3265
|
-
|
|
3281
|
+
credential: await credentialFor(session),
|
|
3266
3282
|
},
|
|
3267
3283
|
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
3268
3284
|
fetchImpl,
|
|
@@ -3368,7 +3384,7 @@ export async function fetchSessionFromApi(
|
|
|
3368
3384
|
} catch {
|
|
3369
3385
|
return { ok: false, reason: "no_session", session: null };
|
|
3370
3386
|
}
|
|
3371
|
-
if (!session
|
|
3387
|
+
if (!isAuthenticated(session)) {
|
|
3372
3388
|
return { ok: false, reason: "not_authenticated", session: null, status: 401 };
|
|
3373
3389
|
}
|
|
3374
3390
|
|
|
@@ -3382,7 +3398,7 @@ export async function fetchSessionFromApi(
|
|
|
3382
3398
|
endpoint,
|
|
3383
3399
|
{
|
|
3384
3400
|
method: "GET",
|
|
3385
|
-
|
|
3401
|
+
credential: await credentialFor(session),
|
|
3386
3402
|
},
|
|
3387
3403
|
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
3388
3404
|
fetchImpl,
|
|
@@ -3447,7 +3463,7 @@ export async function probeSessionAccess(
|
|
|
3447
3463
|
} catch {
|
|
3448
3464
|
return { accessible: false, reason: "no_session" };
|
|
3449
3465
|
}
|
|
3450
|
-
if (!session
|
|
3466
|
+
if (!isAuthenticated(session)) {
|
|
3451
3467
|
return { accessible: false, reason: "not_authenticated" };
|
|
3452
3468
|
}
|
|
3453
3469
|
|
|
@@ -3458,13 +3474,11 @@ export async function probeSessionAccess(
|
|
|
3458
3474
|
|
|
3459
3475
|
let response;
|
|
3460
3476
|
try {
|
|
3461
|
-
response = await
|
|
3477
|
+
response = await credentialedRequest(
|
|
3478
|
+
await credentialFor(session),
|
|
3462
3479
|
endpoint,
|
|
3463
|
-
{
|
|
3464
|
-
|
|
3465
|
-
headers: { Authorization: `Bearer ${session.token}` },
|
|
3466
|
-
},
|
|
3467
|
-
normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS),
|
|
3480
|
+
{ method: "GET" },
|
|
3481
|
+
{ fetchImpl: (target, init) => fetchImpl(target, init, normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS)) },
|
|
3468
3482
|
);
|
|
3469
3483
|
} catch (err) {
|
|
3470
3484
|
return {
|