sentinelayer-cli 0.46.0 → 0.46.2
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/bin/create-sentinelayer.js +2 -2
- package/bin/sentinelayer-cli.js +2 -2
- package/bin/sl.js +2 -2
- package/package.json +1 -1
- package/src/agents/agent-id-path.js +57 -0
- package/src/agents/jules/tools/runtime-audit.js +2 -1
- package/src/ai/aidenid.js +11 -10
- package/src/ai/client.js +2 -1
- package/src/audit/orchestrator.js +7 -1
- package/src/audit/registry.js +9 -1
- package/src/auth/credential-destinations.js +57 -13
- package/src/auth/http.js +4 -0
- package/src/auth/service.js +4 -3
- package/src/board/evidence-resolvers.js +15 -3
- package/src/cli-bootstrap.js +20 -0
- package/src/cli.js +3 -1
- package/src/commands/ai/identity-lifecycle.js +4 -3
- package/src/commands/chat.js +4 -1
- package/src/commands/session.js +51 -17
- package/src/legacy-cli.js +4 -11
- package/src/mcp/session-stdio-server.js +13 -9
- package/src/net/gh-api-path.js +68 -0
- package/src/net/url-path.js +41 -0
- package/src/review/dd-report-email-client.js +2 -3
- package/src/scan/gh-secrets.js +4 -1
- package/src/session/admission-access.js +17 -8
- package/src/session/admission.js +12 -5
- package/src/session/agent-registry.js +4 -5
- package/src/session/checkpoints.js +4 -3
- package/src/session/file-locks.js +8 -6
- package/src/session/invitations.js +2 -1
- package/src/session/listener-process.js +118 -32
- package/src/session/live-source.js +2 -3
- package/src/session/paths.js +51 -2
- package/src/session/store.js +3 -3
- package/src/session/sync.js +34 -26
- package/src/session/tickets.js +13 -6
- package/src/session/title-sync.js +2 -1
package/src/session/sync.js
CHANGED
|
@@ -11,6 +11,7 @@ import { isSessionControlEvent } from "./control-events.js";
|
|
|
11
11
|
import { createSessionMutationHeaders } from "./invitations.js";
|
|
12
12
|
import { messageRevision } from "./message-edits.js";
|
|
13
13
|
import { installTestEgressGuard } from "../net/test-egress-guard.js";
|
|
14
|
+
import { isUrlPathSegment, urlPathSegment } from "../net/url-path.js";
|
|
14
15
|
|
|
15
16
|
// No-op outside tests; inside a test process (or a child it spawned) every fetch
|
|
16
17
|
// to a non-loopback host is refused before a socket opens.
|
|
@@ -1023,7 +1024,7 @@ export async function syncSessionEventToApi(
|
|
|
1023
1024
|
}
|
|
1024
1025
|
|
|
1025
1026
|
const apiBaseUrl = resolveApiBaseUrl(session);
|
|
1026
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
1027
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/events`;
|
|
1027
1028
|
const requestBody = JSON.stringify({
|
|
1028
1029
|
event,
|
|
1029
1030
|
source: "cli",
|
|
@@ -1221,7 +1222,7 @@ async function syncSessionAuxPayload(
|
|
|
1221
1222
|
}
|
|
1222
1223
|
|
|
1223
1224
|
const apiBaseUrl = resolveApiBaseUrl(session);
|
|
1224
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
1225
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}${pathSuffix}`;
|
|
1225
1226
|
try {
|
|
1226
1227
|
const { response } = await fetchJsonWithFullTimeout(
|
|
1227
1228
|
endpoint,
|
|
@@ -1351,7 +1352,7 @@ export async function pollHumanMessages(
|
|
|
1351
1352
|
query.set("since", normalizedSince);
|
|
1352
1353
|
}
|
|
1353
1354
|
query.set("limit", String(Math.max(1, Math.min(HUMAN_MESSAGE_FETCH_LIMIT, normalizePositiveInteger(limit, HUMAN_MESSAGE_FETCH_LIMIT)))));
|
|
1354
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
1355
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/human-messages?${query.toString()}`;
|
|
1355
1356
|
|
|
1356
1357
|
try {
|
|
1357
1358
|
const { response, payload } = await fetchJsonWithFullTimeout(
|
|
@@ -1526,7 +1527,7 @@ export async function pollSessionEvents(
|
|
|
1526
1527
|
"limit",
|
|
1527
1528
|
String(Math.max(1, Math.min(SESSION_EVENT_FETCH_LIMIT, normalizePositiveInteger(limit, SESSION_EVENT_FETCH_LIMIT))))
|
|
1528
1529
|
);
|
|
1529
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
1530
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/events?${query.toString()}`;
|
|
1530
1531
|
|
|
1531
1532
|
try {
|
|
1532
1533
|
const { response, payload } = await fetchJsonWithFullTimeout(
|
|
@@ -1681,7 +1682,7 @@ export async function fetchSessionPresence(
|
|
|
1681
1682
|
}
|
|
1682
1683
|
|
|
1683
1684
|
const apiBaseUrl = resolveApiBaseUrl(session);
|
|
1684
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
1685
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/presence`;
|
|
1685
1686
|
try {
|
|
1686
1687
|
const { response, payload } = await fetchJsonWithFullTimeout(
|
|
1687
1688
|
endpoint,
|
|
@@ -1832,7 +1833,7 @@ export async function renewSessionPresence(
|
|
|
1832
1833
|
}
|
|
1833
1834
|
|
|
1834
1835
|
const apiBaseUrl = resolveApiBaseUrl(session);
|
|
1835
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
1836
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/presence`;
|
|
1836
1837
|
const body = {
|
|
1837
1838
|
agentId: normalizedAgentId,
|
|
1838
1839
|
state: normalizeString(state).toLowerCase() || "idle",
|
|
@@ -1955,7 +1956,7 @@ export async function requestSessionListenerStop(
|
|
|
1955
1956
|
const normalizedIdempotencyKey =
|
|
1956
1957
|
normalizeString(idempotencyKey) || `sl-listener-stop-${randomUUID()}`;
|
|
1957
1958
|
const apiBaseUrl = resolveApiBaseUrl(session);
|
|
1958
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
1959
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/listener-controls/stop`;
|
|
1959
1960
|
const body = normalizedTargetAgentId
|
|
1960
1961
|
? { targetAgentId: normalizedTargetAgentId }
|
|
1961
1962
|
: { broadcast: true };
|
|
@@ -2080,7 +2081,7 @@ export async function updateSessionReadCursor(
|
|
|
2080
2081
|
}
|
|
2081
2082
|
|
|
2082
2083
|
const apiBaseUrl = resolveApiBaseUrl(session);
|
|
2083
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
2084
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/read-cursor`;
|
|
2084
2085
|
const body = {
|
|
2085
2086
|
targetSequenceId:
|
|
2086
2087
|
Number.isFinite(normalizedTargetSequence) && normalizedTargetSequence > 0
|
|
@@ -2225,7 +2226,7 @@ export async function streamSessionEvents(
|
|
|
2225
2226
|
query.set("after", normalizedSince);
|
|
2226
2227
|
}
|
|
2227
2228
|
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
2228
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
2229
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/stream${suffix}`;
|
|
2229
2230
|
const controller = new AbortController();
|
|
2230
2231
|
const normalizedTimeoutMs = normalizePositiveInteger(timeoutMs, DEFAULT_SYNC_TIMEOUT_MS);
|
|
2231
2232
|
const normalizedIdleTimeoutMs = normalizePositiveInteger(idleTimeoutMs, 0);
|
|
@@ -2450,7 +2451,7 @@ export async function pollSessionEventsBefore(
|
|
|
2450
2451
|
"limit",
|
|
2451
2452
|
String(Math.max(1, Math.min(SESSION_EVENT_FETCH_LIMIT, normalizePositiveInteger(limit, 50))))
|
|
2452
2453
|
);
|
|
2453
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
2454
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/events/before?${query.toString()}`;
|
|
2454
2455
|
|
|
2455
2456
|
try {
|
|
2456
2457
|
const { response, payload } = await fetchJsonWithFullTimeout(
|
|
@@ -2571,7 +2572,7 @@ export async function listSessionMessageActions(
|
|
|
2571
2572
|
"limit",
|
|
2572
2573
|
String(Math.max(1, Math.min(SESSION_ACTION_FETCH_LIMIT, normalizePositiveInteger(limit, 200))))
|
|
2573
2574
|
);
|
|
2574
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
2575
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/actions?${query.toString()}`;
|
|
2575
2576
|
|
|
2576
2577
|
try {
|
|
2577
2578
|
const { response, payload } = await fetchJsonWithFullTimeout(
|
|
@@ -2668,7 +2669,7 @@ export async function fetchSessionUsageLedger(
|
|
|
2668
2669
|
"limit",
|
|
2669
2670
|
String(Math.max(1, Math.min(500, normalizePositiveInteger(limit, 500))))
|
|
2670
2671
|
);
|
|
2671
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
2672
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/usage?${query.toString()}`;
|
|
2672
2673
|
|
|
2673
2674
|
try {
|
|
2674
2675
|
const { response, payload } = await fetchJsonWithFullTimeout(
|
|
@@ -2847,7 +2848,10 @@ export async function editSessionMessage(sessionId, {
|
|
|
2847
2848
|
let revision = expectedRevision === null || expectedRevision === undefined ? null : Number(expectedRevision);
|
|
2848
2849
|
const validSequence = Number.isSafeInteger(sequence) && sequence > 0;
|
|
2849
2850
|
const mutationKey = normalizeString(idempotencyKey) || `sl-message-edit-${randomUUID()}`;
|
|
2850
|
-
|
|
2851
|
+
// Every id is checked here, before the request try below: an id that is not one URL path
|
|
2852
|
+
// segment sends nothing and is never counted against the outbound breaker.
|
|
2853
|
+
const idsAreSegments = isUrlPathSegment(sid) && [replyId, messageId].every((id) => !id || isUrlPathSegment(id));
|
|
2854
|
+
if (!idsAreSegments || typeof text !== "string" || !text.trim() || (!replyId && !messageId && !validSequence) ||
|
|
2851
2855
|
(revision !== null && (!Number.isSafeInteger(revision) || revision <= 0)) || mutationKey.length > 128) {
|
|
2852
2856
|
return { ok: false, reason: "invalid_input" };
|
|
2853
2857
|
}
|
|
@@ -2858,7 +2862,11 @@ export async function editSessionMessage(sessionId, {
|
|
|
2858
2862
|
try { auth = await resolveAuthSession({ cwd: targetPath, env: process.env, autoRotate: false }); }
|
|
2859
2863
|
catch { return { ok: false, reason: "no_session" }; }
|
|
2860
2864
|
if (!isAuthenticated(auth)) return { ok: false, reason: "not_authenticated" };
|
|
2861
|
-
const base = `${resolveApiBaseUrl(auth)}/api/v1/sessions/${
|
|
2865
|
+
const base = `${resolveApiBaseUrl(auth)}/api/v1/sessions/${urlPathSegment(sid, { label: "sessionId" })}`;
|
|
2866
|
+
// The edit target: a reply by its id, or a message by its id (checked above, or from the lookup).
|
|
2867
|
+
const targetUrl = (id) => (replyId
|
|
2868
|
+
? `${base}/replies/${urlPathSegment(replyId, { label: "replyId" })}`
|
|
2869
|
+
: `${base}/messages/${urlPathSegment(id, { label: "messageId" })}`);
|
|
2862
2870
|
const credential = await credentialFor(auth);
|
|
2863
2871
|
const failure = (response, payload) => {
|
|
2864
2872
|
const code = normalizeString(payload?.detail?.code || payload?.error?.code || payload?.code);
|
|
@@ -2869,21 +2877,23 @@ export async function editSessionMessage(sessionId, {
|
|
|
2869
2877
|
try {
|
|
2870
2878
|
// Sequence resolution and omitted-revision lookup are one authenticated GET.
|
|
2871
2879
|
if ((!replyId && !messageId) || revision === null) {
|
|
2872
|
-
const
|
|
2873
|
-
?
|
|
2880
|
+
const lookupUrl = replyId || messageId
|
|
2881
|
+
? targetUrl(messageId)
|
|
2882
|
+
: `${base}/messages/by-sequence/${urlPathSegment(sequence, { label: "targetSequenceId" })}`;
|
|
2874
2883
|
const authorQuery = normalizeString(agentId) ? `?agentId=${encodeURIComponent(normalizeString(agentId))}` : "";
|
|
2875
|
-
const { response, payload } = await fetchJsonWithFullTimeout(`${
|
|
2884
|
+
const { response, payload } = await fetchJsonWithFullTimeout(`${lookupUrl}${authorQuery}`, { method: "GET", credential, signal }, timeoutMs, fetchImpl, { readErrorBody: true });
|
|
2876
2885
|
if (!response?.ok || payload?.ok === false) return failure(response, payload);
|
|
2877
2886
|
const current = replyId ? payload?.reply : payload?.event;
|
|
2878
2887
|
const fetchedRevision = Number(current?.messageRevision ?? 1);
|
|
2879
|
-
|
|
2888
|
+
// A message id from the lookup is held to the same rule before it is used in a URL.
|
|
2889
|
+
if (!current?.id || (replyId ? current.id !== replyId : !isUrlPathSegment(current.id)) ||
|
|
2890
|
+
!Number.isSafeInteger(fetchedRevision) || fetchedRevision <= 0) return { ok: false, reason: "invalid_edit_target" };
|
|
2880
2891
|
if (current.canEdit === false) return { ok: false, reason: "MESSAGE_EDIT_FORBIDDEN", status: 403 };
|
|
2881
2892
|
if (!replyId) messageId = current.id;
|
|
2882
2893
|
if (revision === null) revision = fetchedRevision;
|
|
2883
2894
|
}
|
|
2884
|
-
const resource = replyId ? `replies/${encodeURIComponent(replyId)}` : `messages/${encodeURIComponent(messageId)}`;
|
|
2885
2895
|
const body = { text, expectedRevision: revision, ...(normalizeString(agentId) ? { agentId: normalizeString(agentId) } : {}) };
|
|
2886
|
-
const { response, payload } = await fetchJsonWithFullTimeout(
|
|
2896
|
+
const { response, payload } = await fetchJsonWithFullTimeout(targetUrl(messageId), {
|
|
2887
2897
|
method: "PATCH",
|
|
2888
2898
|
headers: {
|
|
2889
2899
|
...createSessionMutationHeaders({ credential, sessionId: sid, routeId: replyId
|
|
@@ -2985,7 +2995,7 @@ export async function createSessionMessageAction(
|
|
|
2985
2995
|
}
|
|
2986
2996
|
|
|
2987
2997
|
const apiBaseUrl = resolveApiBaseUrl(session);
|
|
2988
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
2998
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/actions`;
|
|
2989
2999
|
const body = {
|
|
2990
3000
|
actionType: normalizedActionType,
|
|
2991
3001
|
metadata: metadata && typeof metadata === "object" && !Array.isArray(metadata) ? metadata : {},
|
|
@@ -3143,7 +3153,7 @@ export async function searchSessionEvents(
|
|
|
3143
3153
|
"limit",
|
|
3144
3154
|
String(Math.max(1, Math.min(SESSION_SEARCH_FETCH_LIMIT, normalizePositiveInteger(limit, 20))))
|
|
3145
3155
|
);
|
|
3146
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
3156
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/events/search?${queryParams.toString()}`;
|
|
3147
3157
|
|
|
3148
3158
|
try {
|
|
3149
3159
|
const { response, payload } = await fetchJsonWithFullTimeout(
|
|
@@ -3389,7 +3399,7 @@ export async function fetchSessionFromApi(
|
|
|
3389
3399
|
}
|
|
3390
3400
|
|
|
3391
3401
|
const apiBaseUrl = resolveApiBaseUrl(session);
|
|
3392
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
3402
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}`;
|
|
3393
3403
|
|
|
3394
3404
|
let response;
|
|
3395
3405
|
let body;
|
|
@@ -3468,9 +3478,7 @@ export async function probeSessionAccess(
|
|
|
3468
3478
|
}
|
|
3469
3479
|
|
|
3470
3480
|
const apiBaseUrl = resolveApiBaseUrl(session);
|
|
3471
|
-
const endpoint = `${apiBaseUrl}/api/v1/sessions/${
|
|
3472
|
-
normalizedSessionId,
|
|
3473
|
-
)}/events?limit=1`;
|
|
3481
|
+
const endpoint = `${apiBaseUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/events?limit=1`;
|
|
3474
3482
|
|
|
3475
3483
|
let response;
|
|
3476
3484
|
try {
|
package/src/session/tickets.js
CHANGED
|
@@ -22,6 +22,7 @@ import process from "node:process";
|
|
|
22
22
|
import { checkedTransport, credentialFor, isAuthenticated } from "../auth/credential-destinations.js";
|
|
23
23
|
import { requestJson, requestJsonMutation } from "../auth/http.js";
|
|
24
24
|
import { resolveActiveAuthSession } from "../auth/service.js";
|
|
25
|
+
import { urlPathSegment } from "../net/url-path.js";
|
|
25
26
|
|
|
26
27
|
function normalizeString(value) {
|
|
27
28
|
return String(value ?? "").trim();
|
|
@@ -75,8 +76,14 @@ async function auth(targetPath, resolveAuthSession) {
|
|
|
75
76
|
return { credential: await credentialFor(session), apiUrl: normalizeString(session.apiUrl).replace(/\/+$/, "") };
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
/** The room's tickets URL; `route` is a fixed collection route ("/events"). */
|
|
80
|
+
function ticketsUrl(apiUrl, sessionId, route = "") {
|
|
81
|
+
return `${apiUrl}/api/v1/sessions/${urlPathSegment(sessionId, { label: "sessionId" })}/tickets${route}`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** One ticket's URL: the raw ticket id is encoded here; `route` is a fixed route ("/claim"). */
|
|
85
|
+
function ticketUrl(apiUrl, sessionId, ticketId, route) {
|
|
86
|
+
return `${ticketsUrl(apiUrl, sessionId)}/${urlPathSegment(ticketId, { label: "ticketId" })}${route}`;
|
|
80
87
|
}
|
|
81
88
|
|
|
82
89
|
/** The Idempotency-Key for this logical write: reused until its response is stored. */
|
|
@@ -226,7 +233,7 @@ export async function claimTicket(
|
|
|
226
233
|
const statePath = ticketLeasePath(sessionId, ticketId, identity, { homeDir });
|
|
227
234
|
const { credential, apiUrl } = await auth(targetPath, resolveAuthSession);
|
|
228
235
|
const { key } = await operationKey(statePath, "claim", String(expectedVersion ?? "any"));
|
|
229
|
-
const response = await mutate(
|
|
236
|
+
const response = await mutate(ticketUrl(apiUrl, sessionId, ticketId, "/claim"), {
|
|
230
237
|
credential, key, operation: "claim", requestMutation,
|
|
231
238
|
body: expectedVersion == null ? {} : { expectedVersion: Number(expectedVersion) },
|
|
232
239
|
});
|
|
@@ -262,7 +269,7 @@ export async function renewTicketLease(
|
|
|
262
269
|
const held = await heldLease(statePath);
|
|
263
270
|
const { credential, apiUrl } = await auth(targetPath, resolveAuthSession);
|
|
264
271
|
const { key } = await operationKey(statePath, "renew", `${held.leaseId}:${held.fence}:${held.ticketVersion}`);
|
|
265
|
-
const response = await mutate(
|
|
272
|
+
const response = await mutate(ticketUrl(apiUrl, sessionId, ticketId, "/lease/renew"), {
|
|
266
273
|
credential, key, operation: "renew", requestMutation, body: { leaseId: held.leaseId, fence: held.fence },
|
|
267
274
|
});
|
|
268
275
|
await writeState(statePath, leaseState(response));
|
|
@@ -278,7 +285,7 @@ export async function releaseTicketLease(
|
|
|
278
285
|
const held = await heldLease(statePath);
|
|
279
286
|
const { credential, apiUrl } = await auth(targetPath, resolveAuthSession);
|
|
280
287
|
const { key } = await operationKey(statePath, "release", `${held.leaseId}:${held.fence}:${held.ticketVersion}`);
|
|
281
|
-
const response = await mutate(
|
|
288
|
+
const response = await mutate(ticketUrl(apiUrl, sessionId, ticketId, "/lease/release"), {
|
|
282
289
|
credential, key, operation: "release", requestMutation,
|
|
283
290
|
body: { leaseId: held.leaseId, fence: held.fence, expectedVersion: held.ticketVersion, ...(reason ? { reason } : {}) },
|
|
284
291
|
});
|
|
@@ -303,7 +310,7 @@ export async function submitTicket(
|
|
|
303
310
|
const held = await heldLease(statePath);
|
|
304
311
|
const { credential, apiUrl } = await auth(targetPath, resolveAuthSession);
|
|
305
312
|
const { key } = await operationKey(statePath, "submit", `${held.leaseId}:${held.fence}:${held.ticketVersion}`);
|
|
306
|
-
const response = await mutate(
|
|
313
|
+
const response = await mutate(ticketUrl(apiUrl, sessionId, ticketId, "/submit"), {
|
|
307
314
|
credential, key, operation: "submit", requestMutation,
|
|
308
315
|
body: {
|
|
309
316
|
leaseId: held.leaseId,
|
|
@@ -4,6 +4,7 @@ import { checkedTransport, credentialFor, isAuthenticated } from "../auth/creden
|
|
|
4
4
|
import { SentinelayerApiError, requestJsonMutation } from "../auth/http.js";
|
|
5
5
|
import { resolveActiveAuthSession } from "../auth/service.js";
|
|
6
6
|
import { recordSessionRemoteTitleSync } from "./store.js";
|
|
7
|
+
import { urlPathSegment } from "../net/url-path.js";
|
|
7
8
|
|
|
8
9
|
const DEFAULT_TITLE_SYNC_TIMEOUT_MS = 2_000;
|
|
9
10
|
const DEFAULT_TITLE_SYNC_RETRY_DELAY_MS = 200;
|
|
@@ -78,7 +79,7 @@ export async function pushSessionTitleToApi(
|
|
|
78
79
|
}
|
|
79
80
|
const apiUrl = String(session.apiUrl).replace(/\/+$/, "");
|
|
80
81
|
const result = await checkedTransport(requestMutation)(
|
|
81
|
-
`${apiUrl}/api/v1/sessions/${
|
|
82
|
+
`${apiUrl}/api/v1/sessions/${urlPathSegment(normalizedSessionId, { label: "sessionId" })}/title`,
|
|
82
83
|
{
|
|
83
84
|
method: "POST",
|
|
84
85
|
operationName: "session.set_title",
|