@cmmd-center/forge 0.13.14 → 0.13.15
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/dist/bin.cjs +437 -163
- package/dist/bin.mjs +460 -187
- package/dist/client/assets/{DiffPanel-P3t5Fq1q.js → DiffPanel-D-NIms_l.js} +2 -2
- package/dist/client/assets/{DiffPanel.logic-BXQ1C0-e.js → DiffPanel.logic-DwoeNGnH.js} +2 -2
- package/dist/client/assets/{DiffPanelShell-3EpWEr7p.js → DiffPanelShell-BI5poBl7.js} +3 -3
- package/dist/client/assets/{DiffWorkerPoolProvider-3_hkcEDq.js → DiffWorkerPoolProvider-DHNvSWPo.js} +2 -2
- package/dist/client/assets/{PullRequestCodePanel-B2T6MPcE.js → PullRequestCodePanel-apgvV8hi.js} +2 -2
- package/dist/client/assets/{Virtualizer-DCt36RGD.js → Virtualizer-Cm4G15Gy.js} +2 -2
- package/dist/client/assets/{index-DvwXD11B.js → index-DdqgGbOQ.js} +60 -60
- package/dist/client/index.html +4 -4
- package/dist/forge-build.json +1 -1
- package/dist/{open-D8mGyWmt.mjs → open-FIgTDosf.mjs} +3 -3
- package/package.json +1 -1
package/dist/bin.cjs
CHANGED
|
@@ -27208,6 +27208,44 @@ function encodeCmmdRuntimeWorktreeLaunchActivityPayload(kind, payload) {
|
|
|
27208
27208
|
function isCmmdRuntimeWorktreeLaunchActivityKind(kind) {
|
|
27209
27209
|
return WORKTREE_LAUNCH_ACTIVITY_KINDS.has(kind);
|
|
27210
27210
|
}
|
|
27211
|
+
/**
|
|
27212
|
+
* A plan update rides the `tool_call` event kind because the forge-context
|
|
27213
|
+
* contract has no plan event kind, the same trade the launch rows make. The
|
|
27214
|
+
* steps are carried verbatim under a versioned marker so a cloud client can
|
|
27215
|
+
* rebuild the composer plan banner from the durable replica. (#3319)
|
|
27216
|
+
*/
|
|
27217
|
+
const CMMD_RUNTIME_PLAN_ACTIVITY_KIND = "turn.plan.updated";
|
|
27218
|
+
function encodeCmmdRuntimePlanActivityPayload(kind, payload) {
|
|
27219
|
+
return {
|
|
27220
|
+
...isRecord$23(payload) ? payload : { sourcePayload: payload },
|
|
27221
|
+
forgePlanActivity: {
|
|
27222
|
+
schemaVersion: 1,
|
|
27223
|
+
source: "native",
|
|
27224
|
+
kind
|
|
27225
|
+
}
|
|
27226
|
+
};
|
|
27227
|
+
}
|
|
27228
|
+
function decodeCmmdRuntimePlanActivity(payload) {
|
|
27229
|
+
if (!isRecord$23(payload)) return null;
|
|
27230
|
+
const metadata = payload["forgePlanActivity"];
|
|
27231
|
+
if (!isRecord$23(metadata)) return null;
|
|
27232
|
+
if (metadata["schemaVersion"] !== 1 || metadata["source"] !== "native" || metadata["kind"] !== "turn.plan.updated") return null;
|
|
27233
|
+
const rawPlan = payload["plan"];
|
|
27234
|
+
return {
|
|
27235
|
+
kind: CMMD_RUNTIME_PLAN_ACTIVITY_KIND,
|
|
27236
|
+
plan: Array.isArray(rawPlan) ? rawPlan.flatMap((entry) => {
|
|
27237
|
+
if (!isRecord$23(entry)) return [];
|
|
27238
|
+
const step = entry["step"];
|
|
27239
|
+
const status = entry["status"];
|
|
27240
|
+
if (typeof step !== "string" || step.trim().length === 0) return [];
|
|
27241
|
+
if (status !== "pending" && status !== "inProgress" && status !== "completed") return [];
|
|
27242
|
+
return [{
|
|
27243
|
+
step,
|
|
27244
|
+
status
|
|
27245
|
+
}];
|
|
27246
|
+
}) : []
|
|
27247
|
+
};
|
|
27248
|
+
}
|
|
27211
27249
|
//#endregion
|
|
27212
27250
|
//#region ../../packages/contracts/src/cmmdProjectPatch.ts
|
|
27213
27251
|
const CMMD_PROJECT_PATCH_FIELDS = {
|
|
@@ -66380,7 +66418,7 @@ function normalizeNumberish(value) {
|
|
|
66380
66418
|
}
|
|
66381
66419
|
//#endregion
|
|
66382
66420
|
//#region package.json
|
|
66383
|
-
var version$1 = "0.13.
|
|
66421
|
+
var version$1 = "0.13.15";
|
|
66384
66422
|
//#endregion
|
|
66385
66423
|
//#region src/sentry.ts
|
|
66386
66424
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -67260,6 +67298,121 @@ function verifyCmmdForgeCloudOwnerAssertion(token, publicKeyPem, expectedKeyId,
|
|
|
67260
67298
|
};
|
|
67261
67299
|
}
|
|
67262
67300
|
//#endregion
|
|
67301
|
+
//#region src/observability/Attributes.ts
|
|
67302
|
+
function isPlainObject$1(value) {
|
|
67303
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
67304
|
+
}
|
|
67305
|
+
function markSeen(value, seen) {
|
|
67306
|
+
if (seen.has(value)) return true;
|
|
67307
|
+
seen.add(value);
|
|
67308
|
+
return false;
|
|
67309
|
+
}
|
|
67310
|
+
function normalizeJsonValue(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
67311
|
+
if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value ?? null;
|
|
67312
|
+
if (typeof value === "bigint") return value.toString();
|
|
67313
|
+
if (value instanceof Date) return Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString();
|
|
67314
|
+
if (value instanceof Error) return {
|
|
67315
|
+
name: value.name,
|
|
67316
|
+
message: value.message,
|
|
67317
|
+
...value.stack ? { stack: value.stack } : {}
|
|
67318
|
+
};
|
|
67319
|
+
if (Array.isArray(value)) {
|
|
67320
|
+
if (markSeen(value, seen)) return "[Circular]";
|
|
67321
|
+
return value.map((entry) => normalizeJsonValue(entry, seen));
|
|
67322
|
+
}
|
|
67323
|
+
if (value instanceof Map) {
|
|
67324
|
+
if (markSeen(value, seen)) return "[Circular]";
|
|
67325
|
+
return Object.fromEntries(Array.from(value.entries(), ([key, entryValue]) => [String(key), normalizeJsonValue(entryValue, seen)]));
|
|
67326
|
+
}
|
|
67327
|
+
if (value instanceof Set) {
|
|
67328
|
+
if (markSeen(value, seen)) return "[Circular]";
|
|
67329
|
+
return Array.from(value.values(), (entry) => normalizeJsonValue(entry, seen));
|
|
67330
|
+
}
|
|
67331
|
+
if (!isPlainObject$1(value)) return String(value);
|
|
67332
|
+
if (markSeen(value, seen)) return "[Circular]";
|
|
67333
|
+
return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [key, normalizeJsonValue(entryValue, seen)]));
|
|
67334
|
+
}
|
|
67335
|
+
function compactTraceAttributes(attributes) {
|
|
67336
|
+
return Object.fromEntries(Object.entries(attributes).filter(([, value]) => value !== void 0).map(([key, value]) => [key, normalizeJsonValue(value)]));
|
|
67337
|
+
}
|
|
67338
|
+
function compactMetricAttributes(attributes) {
|
|
67339
|
+
return Object.fromEntries(Object.entries(attributes).flatMap(([key, value]) => {
|
|
67340
|
+
if (value === void 0 || value === null) return [];
|
|
67341
|
+
if (typeof value === "string") return [[key, value]];
|
|
67342
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return [[key, String(value)]];
|
|
67343
|
+
return [];
|
|
67344
|
+
}));
|
|
67345
|
+
}
|
|
67346
|
+
function outcomeFromExit(exit) {
|
|
67347
|
+
if (require_Schema$1.isSuccess(exit)) return "success";
|
|
67348
|
+
return require_Schema$1.hasInterruptsOnly(exit.cause) ? "interrupt" : "failure";
|
|
67349
|
+
}
|
|
67350
|
+
function normalizeModelMetricLabel(model) {
|
|
67351
|
+
const normalized = model?.trim().toLowerCase();
|
|
67352
|
+
if (!normalized) return;
|
|
67353
|
+
if (normalized.includes("gpt")) return "gpt";
|
|
67354
|
+
if (normalized.includes("claude")) return "claude";
|
|
67355
|
+
if (normalized.includes("gemini")) return "gemini";
|
|
67356
|
+
return "other";
|
|
67357
|
+
}
|
|
67358
|
+
//#endregion
|
|
67359
|
+
//#region src/observability/Metrics.ts
|
|
67360
|
+
const rpcRequestsTotal = require_Schema$1.counter("t3_rpc_requests_total", { description: "Total RPC requests handled by the websocket RPC server." });
|
|
67361
|
+
const rpcRequestDuration = require_Schema$1.timer("t3_rpc_request_duration", { description: "RPC request handling duration." });
|
|
67362
|
+
const orchestrationCommandsTotal = require_Schema$1.counter("t3_orchestration_commands_total", { description: "Total orchestration commands dispatched." });
|
|
67363
|
+
const orchestrationCommandDuration = require_Schema$1.timer("t3_orchestration_command_duration", { description: "Orchestration command dispatch duration." });
|
|
67364
|
+
const orchestrationCommandAckDuration = require_Schema$1.timer("t3_orchestration_command_ack_duration", { description: "Time from orchestration command dispatch to the first committed domain event emitted for that command." });
|
|
67365
|
+
const orchestrationEventsProcessedTotal = require_Schema$1.counter("t3_orchestration_events_processed_total", { description: "Total orchestration intent events processed by runtime reactors." });
|
|
67366
|
+
const providerSessionsTotal = require_Schema$1.counter("t3_provider_sessions_total", { description: "Total provider session lifecycle operations." });
|
|
67367
|
+
const providerTurnsTotal = require_Schema$1.counter("t3_provider_turns_total", { description: "Total provider turn lifecycle operations." });
|
|
67368
|
+
const providerTurnDuration = require_Schema$1.timer("t3_provider_turn_duration", { description: "Provider turn request duration." });
|
|
67369
|
+
const providerRuntimeEventsTotal = require_Schema$1.counter("t3_provider_runtime_events_total", { description: "Total canonical provider runtime events processed." });
|
|
67370
|
+
const gitCommandsTotal = require_Schema$1.counter("t3_git_commands_total", { description: "Total git commands executed by the server runtime." });
|
|
67371
|
+
const gitCommandDuration = require_Schema$1.timer("t3_git_command_duration", { description: "Git command execution duration." });
|
|
67372
|
+
const terminalSessionsTotal = require_Schema$1.counter("t3_terminal_sessions_total", { description: "Total terminal sessions started." });
|
|
67373
|
+
const browserSessionsReapedTotal = require_Schema$1.counter("browser_sessions_reaped_total", { description: "Browser sessions and Chromium processes reclaimed by the orphan reaper, by reason (orphan | ttl | stale_profile)." });
|
|
67374
|
+
const terminalRestartsTotal = require_Schema$1.counter("t3_terminal_restarts_total", { description: "Total terminal restart requests handled." });
|
|
67375
|
+
const runtimeReplicationOutboxDepth = require_Schema$1.gauge("t3_runtime_replication_outbox_depth", { description: "Current runtime replication outbox items by durable state." });
|
|
67376
|
+
const runtimeReplicationOutboxOldestEventAge = require_Schema$1.gauge("t3_runtime_replication_outbox_oldest_event_age_seconds", { description: "Age in seconds of the oldest undelivered runtime replication payload." });
|
|
67377
|
+
const runtimeReplicationPublicationAttemptsTotal = require_Schema$1.counter("t3_runtime_replication_publication_attempts_total", { description: "Total canonical runtime replication publication attempts." });
|
|
67378
|
+
const runtimeReplicationPublicationRetriesTotal = require_Schema$1.counter("t3_runtime_replication_publication_retries_total", { description: "Total runtime replication retries scheduled after recoverable failures." });
|
|
67379
|
+
const runtimeReplicationPublicationLatency = require_Schema$1.timer("t3_runtime_replication_publication_latency", { description: "Time from durable enqueue to canonical CMMD acknowledgement." });
|
|
67380
|
+
const cmmdCredentialRefreshTotal = require_Schema$1.counter("t3_cmmd_credential_refresh_total", { description: "CMMD JWT refresh attempts for a Forge session, by rung (self-refresh | grant-redemption) and outcome (attempted | succeeded)." });
|
|
67381
|
+
const cmmdRotationFenceAppliedTotal = require_Schema$1.counter("t3_cmmd_rotation_fence_applied_total", { description: "Sessions Forge refused to rotate CMMD credentials for, because the rotation fence applied." });
|
|
67382
|
+
const cmmdCredentialRowDeletedTotal = require_Schema$1.counter("t3_cmmd_credential_row_deleted_total", { description: "command_access_tokens rows actually deleted by clearUnrecoverableCmmdAuthForSession, by reason." });
|
|
67383
|
+
const metricAttributes = (attributes) => Object.entries(compactMetricAttributes(attributes));
|
|
67384
|
+
const increment = (metric, attributes, amount = 1) => require_Schema$1.update(require_Schema$1.withAttributes(metric, metricAttributes(attributes)), amount);
|
|
67385
|
+
const withMetricsImpl = (effect, options) => require_Schema$1.gen(function* () {
|
|
67386
|
+
const startedAt = Date.now();
|
|
67387
|
+
const exit$2 = yield* require_Schema$1.exit(effect);
|
|
67388
|
+
const duration = require_Schema$1.millis(Math.max(0, Date.now() - startedAt));
|
|
67389
|
+
const baseAttributes = typeof options.attributes === "function" ? options.attributes() : options.attributes ?? {};
|
|
67390
|
+
if (options.timer) yield* require_Schema$1.update(require_Schema$1.withAttributes(options.timer, metricAttributes(baseAttributes)), duration);
|
|
67391
|
+
if (options.counter) {
|
|
67392
|
+
const outcome = outcomeFromExit(exit$2);
|
|
67393
|
+
yield* require_Schema$1.update(require_Schema$1.withAttributes(options.counter, metricAttributes({
|
|
67394
|
+
...baseAttributes,
|
|
67395
|
+
outcome,
|
|
67396
|
+
...options.outcomeAttributes ? options.outcomeAttributes(outcome) : {}
|
|
67397
|
+
})), 1);
|
|
67398
|
+
}
|
|
67399
|
+
if (require_Schema$1.isSuccess(exit$2)) return exit$2.value;
|
|
67400
|
+
return yield* require_Schema$1.failCause(exit$2.cause);
|
|
67401
|
+
});
|
|
67402
|
+
const withMetrics = require_Schema$1.dual(2, withMetricsImpl);
|
|
67403
|
+
const providerMetricAttributes = (provider, extra) => compactMetricAttributes({
|
|
67404
|
+
provider,
|
|
67405
|
+
...extra
|
|
67406
|
+
});
|
|
67407
|
+
const providerTurnMetricAttributes = (input) => {
|
|
67408
|
+
const modelFamily = normalizeModelMetricLabel(input.model);
|
|
67409
|
+
return compactMetricAttributes({
|
|
67410
|
+
provider: input.provider,
|
|
67411
|
+
...modelFamily ? { modelFamily } : {},
|
|
67412
|
+
...input.extra
|
|
67413
|
+
});
|
|
67414
|
+
};
|
|
67415
|
+
//#endregion
|
|
67263
67416
|
//#region src/persistence/Errors.ts
|
|
67264
67417
|
var PersistenceSqlError = class extends require_Schema$1.TaggedErrorClass()("PersistenceSqlError", {
|
|
67265
67418
|
operation: require_Schema$1.String,
|
|
@@ -67950,8 +68103,14 @@ const scheduleExpiredForgeCloudOwnerCredentialMaintenance = require_Schema$1.sle
|
|
|
67950
68103
|
*
|
|
67951
68104
|
* Clearing every active owner row unconditionally (the pre-CMMD-1871 behaviour)
|
|
67952
68105
|
* turned a single expired tab into a sign-out on every browser and device.
|
|
68106
|
+
*
|
|
68107
|
+
* Destroys a refresh token good for months, so every call is logged with a
|
|
68108
|
+
* `reason` naming the caller's own decision (never the credential itself) and
|
|
68109
|
+
* the actual row count deleted, at info level so it shows up without raising
|
|
68110
|
+
* the server's log level. `reason` is optional only so a caller that has not
|
|
68111
|
+
* been updated yet still compiles; every current caller passes one.
|
|
67953
68112
|
*/
|
|
67954
|
-
const clearUnrecoverableCmmdAuthForSession = (sessionId) => require_Schema$1.gen(function* () {
|
|
68113
|
+
const clearUnrecoverableCmmdAuthForSession = (sessionId, reason) => require_Schema$1.gen(function* () {
|
|
67955
68114
|
const sql = yield* require_SqlError.SqlClient;
|
|
67956
68115
|
const rows = yield* sql`
|
|
67957
68116
|
DELETE FROM command_access_tokens
|
|
@@ -67976,6 +68135,14 @@ const clearUnrecoverableCmmdAuthForSession = (sessionId) => require_Schema$1.gen
|
|
|
67976
68135
|
`.pipe(require_Schema$1.mapError(toPersistenceSqlError("CommandAccessTokenStore.clearUnrecoverableCmmdAuth:query")));
|
|
67977
68136
|
forgetCommandAccessTokenCachesForSession(sessionId);
|
|
67978
68137
|
for (const row of rows) forgetCommandAccessTokenCachesForSession(row.sessionId);
|
|
68138
|
+
if (rows.length > 0) {
|
|
68139
|
+
yield* require_Schema$1.logInfo("CMMD credential row deleted.", {
|
|
68140
|
+
sessionId,
|
|
68141
|
+
reason: reason ?? "unspecified",
|
|
68142
|
+
rowsDeleted: rows.length
|
|
68143
|
+
});
|
|
68144
|
+
yield* require_Schema$1.update(require_Schema$1.withAttributes(cmmdCredentialRowDeletedTotal, metricAttributes({ reason: reason ?? "unspecified" })), rows.length);
|
|
68145
|
+
}
|
|
67979
68146
|
});
|
|
67980
68147
|
/**
|
|
67981
68148
|
* Retire refresh credentials copied into historical desktop bearer sessions.
|
|
@@ -68031,6 +68198,23 @@ const isDesktopBearerAuthSession = (sessionId) => require_Schema$1.gen(function*
|
|
|
68031
68198
|
`.pipe(require_Schema$1.mapError(toPersistenceSqlError("CommandAccessTokenStore.isDesktopBearerSession:query")));
|
|
68032
68199
|
return rows[0]?.method === "bearer-session-token" && rows[0].clientDeviceType === "desktop";
|
|
68033
68200
|
});
|
|
68201
|
+
/**
|
|
68202
|
+
* The device type a session's bearer bootstrap declared, for log lines only.
|
|
68203
|
+
*
|
|
68204
|
+
* Mirrors `isDesktopBearerAuthSession`'s read but returns the raw column
|
|
68205
|
+
* instead of collapsing it into a rotation verdict — a log line wants to say
|
|
68206
|
+
* what made the request, not whether Forge may rotate for it. Never fails:
|
|
68207
|
+
* an unreadable or missing row logs as `"unknown"` rather than losing the
|
|
68208
|
+
* decision-point log line it is attached to.
|
|
68209
|
+
*/
|
|
68210
|
+
const readClientDeviceTypeForSession = (sessionId) => require_Schema$1.gen(function* () {
|
|
68211
|
+
return (yield* (yield* require_SqlError.SqlClient)`
|
|
68212
|
+
SELECT client_device_type AS "clientDeviceType"
|
|
68213
|
+
FROM auth_sessions
|
|
68214
|
+
WHERE session_id = ${sessionId}
|
|
68215
|
+
LIMIT 1
|
|
68216
|
+
`.pipe(require_Schema$1.mapError(toPersistenceSqlError("CommandAccessTokenStore.readClientDeviceType:query"))))[0]?.clientDeviceType ?? "unknown";
|
|
68217
|
+
}).pipe(require_Schema$1.orElseSucceed(() => "unknown"));
|
|
68034
68218
|
//#endregion
|
|
68035
68219
|
//#region src/auth/http/oauthCookies.ts
|
|
68036
68220
|
const OAUTH_STATE_COOKIE = "forge_oauth_state";
|
|
@@ -68233,9 +68417,21 @@ const authLogoutRouteLayer = add$1("POST", "/api/auth/logout", require_Schema$1.
|
|
|
68233
68417
|
onSuccess: (session) => session.sessionId
|
|
68234
68418
|
}));
|
|
68235
68419
|
if (sessionId) {
|
|
68420
|
+
const deviceType = yield* readClientDeviceTypeForSession(sessionId);
|
|
68421
|
+
yield* require_Schema$1.logInfo("Auth logout.", {
|
|
68422
|
+
sessionId,
|
|
68423
|
+
deviceType,
|
|
68424
|
+
outcome: "session-cleared",
|
|
68425
|
+
reason: cloudShell ? "forge-cloud-shell-session" : "local-session"
|
|
68426
|
+
});
|
|
68236
68427
|
if (!cloudShell) yield* sessions.revoke(sessionId).pipe(require_Schema$1.ignore({ log: false }));
|
|
68237
68428
|
yield* clearCommandAccessTokenForSession(sessionId).pipe(require_Schema$1.ignore({ log: false }));
|
|
68238
|
-
}
|
|
68429
|
+
} else yield* require_Schema$1.logInfo("Auth logout.", {
|
|
68430
|
+
sessionId: null,
|
|
68431
|
+
deviceType: "unknown",
|
|
68432
|
+
outcome: "no-op",
|
|
68433
|
+
reason: "no authenticated session presented"
|
|
68434
|
+
});
|
|
68239
68435
|
const secure = shouldUseSecureCookie(forgePublicUrlForRequest(request));
|
|
68240
68436
|
let response = yield* require_HttpServer.jsonUnsafe({ loggedOut: true }, { status: 200 }).pipe(require_HttpServer.setCookie(sessions.cookieName, "", {
|
|
68241
68437
|
expires: /* @__PURE__ */ new Date(0),
|
|
@@ -68834,16 +69030,32 @@ const authCommandBearerBootstrapRouteLayer = add$1("POST", "/api/auth/bootstrap/
|
|
|
68834
69030
|
if (limited) return limited;
|
|
68835
69031
|
const serverAuth = yield* ServerAuth;
|
|
68836
69032
|
const sessions = yield* SessionCredentialService;
|
|
68837
|
-
if (serverAuth.isForgeCloudShell?.() === true)
|
|
68838
|
-
|
|
68839
|
-
|
|
68840
|
-
|
|
69033
|
+
if (serverAuth.isForgeCloudShell?.() === true) {
|
|
69034
|
+
yield* require_Schema$1.logWarning("CMMD bearer bootstrap failed.", {
|
|
69035
|
+
deviceType: "unknown",
|
|
69036
|
+
outcome: "failed",
|
|
69037
|
+
reason: "forge-cloud-shell-rejects-non-cmmd-bootstrap"
|
|
69038
|
+
});
|
|
69039
|
+
return yield* new AuthError({
|
|
69040
|
+
message: "Forge Cloud accepts owner assertions from CMMD only.",
|
|
69041
|
+
status: 403
|
|
69042
|
+
});
|
|
69043
|
+
}
|
|
68841
69044
|
const payload = yield* require_HttpServer.schemaBodyJson(CommandBootstrapInput).pipe(require_Schema$1.mapError((cause) => new AuthError({
|
|
68842
69045
|
message: "Invalid CMMD OAuth bearer bootstrap payload.",
|
|
68843
69046
|
status: 400,
|
|
68844
69047
|
cause
|
|
69048
|
+
})), require_Schema$1.tapError(() => require_Schema$1.logWarning("CMMD bearer bootstrap failed.", {
|
|
69049
|
+
deviceType: "unknown",
|
|
69050
|
+
outcome: "failed",
|
|
69051
|
+
reason: "invalid-payload"
|
|
69052
|
+
})));
|
|
69053
|
+
const deviceType = payload.clientDeviceType ?? "desktop";
|
|
69054
|
+
const validated = yield* validateCommandBootstrapPayload(payload).pipe(require_Schema$1.tapError(() => require_Schema$1.logWarning("CMMD bearer bootstrap failed.", {
|
|
69055
|
+
deviceType,
|
|
69056
|
+
outcome: "failed",
|
|
69057
|
+
reason: "invalid-command-bootstrap-payload"
|
|
68845
69058
|
})));
|
|
68846
|
-
const validated = yield* validateCommandBootstrapPayload(payload);
|
|
68847
69059
|
const sessionSubject = resolveCommandOAuthSubject(validated.subjectUserId);
|
|
68848
69060
|
const issued = yield* sessions.issue({
|
|
68849
69061
|
method: "bearer-session-token",
|
|
@@ -68856,20 +69068,34 @@ const authCommandBearerBootstrapRouteLayer = add$1("POST", "/api/auth/bootstrap/
|
|
|
68856
69068
|
}).pipe(require_Schema$1.mapError((cause) => new AuthError({
|
|
68857
69069
|
message: "Failed to issue authenticated bearer session.",
|
|
68858
69070
|
cause
|
|
69071
|
+
})), require_Schema$1.tapError(() => require_Schema$1.logWarning("CMMD bearer bootstrap failed.", {
|
|
69072
|
+
deviceType,
|
|
69073
|
+
outcome: "failed",
|
|
69074
|
+
reason: "session-issuance-failed"
|
|
68859
69075
|
})));
|
|
68860
69076
|
const cmmdIdentity = yield* persistCommandBootstrapSessionArtifacts({
|
|
68861
69077
|
sessionId: issued.sessionId,
|
|
68862
69078
|
payload,
|
|
68863
69079
|
cmmdIdentity: validated.cmmdIdentity
|
|
68864
69080
|
});
|
|
68865
|
-
if (
|
|
69081
|
+
if (deviceType === "desktop" && !payload.refreshToken?.trim()) yield* clearDesktopBearerRefreshTokensForSubject({
|
|
68866
69082
|
subject: sessionSubject,
|
|
68867
69083
|
clientUserAgent: request.headers["user-agent"]
|
|
68868
69084
|
}).pipe(require_Schema$1.mapError((cause) => new AuthError({
|
|
68869
69085
|
message: "Failed to retire legacy desktop refresh credentials.",
|
|
68870
69086
|
status: 500,
|
|
68871
69087
|
cause
|
|
69088
|
+
})), require_Schema$1.tapError(() => require_Schema$1.logWarning("CMMD bearer bootstrap failed.", {
|
|
69089
|
+
sessionId: issued.sessionId,
|
|
69090
|
+
deviceType,
|
|
69091
|
+
outcome: "failed",
|
|
69092
|
+
reason: "legacy-desktop-refresh-retirement-failed"
|
|
68872
69093
|
})));
|
|
69094
|
+
yield* require_Schema$1.logInfo("CMMD bearer bootstrap succeeded.", {
|
|
69095
|
+
sessionId: issued.sessionId,
|
|
69096
|
+
deviceType,
|
|
69097
|
+
outcome: "succeeded"
|
|
69098
|
+
});
|
|
68873
69099
|
return require_HttpServer.jsonUnsafe({
|
|
68874
69100
|
authenticated: true,
|
|
68875
69101
|
role: issued.role,
|
|
@@ -70177,64 +70403,6 @@ function parseAttachmentIdFromRelativePath(relativePath) {
|
|
|
70177
70403
|
return id.length > 0 && !id.includes(".") ? id : null;
|
|
70178
70404
|
}
|
|
70179
70405
|
//#endregion
|
|
70180
|
-
//#region src/observability/Attributes.ts
|
|
70181
|
-
function isPlainObject$1(value) {
|
|
70182
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
70183
|
-
}
|
|
70184
|
-
function markSeen(value, seen) {
|
|
70185
|
-
if (seen.has(value)) return true;
|
|
70186
|
-
seen.add(value);
|
|
70187
|
-
return false;
|
|
70188
|
-
}
|
|
70189
|
-
function normalizeJsonValue(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
70190
|
-
if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value ?? null;
|
|
70191
|
-
if (typeof value === "bigint") return value.toString();
|
|
70192
|
-
if (value instanceof Date) return Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString();
|
|
70193
|
-
if (value instanceof Error) return {
|
|
70194
|
-
name: value.name,
|
|
70195
|
-
message: value.message,
|
|
70196
|
-
...value.stack ? { stack: value.stack } : {}
|
|
70197
|
-
};
|
|
70198
|
-
if (Array.isArray(value)) {
|
|
70199
|
-
if (markSeen(value, seen)) return "[Circular]";
|
|
70200
|
-
return value.map((entry) => normalizeJsonValue(entry, seen));
|
|
70201
|
-
}
|
|
70202
|
-
if (value instanceof Map) {
|
|
70203
|
-
if (markSeen(value, seen)) return "[Circular]";
|
|
70204
|
-
return Object.fromEntries(Array.from(value.entries(), ([key, entryValue]) => [String(key), normalizeJsonValue(entryValue, seen)]));
|
|
70205
|
-
}
|
|
70206
|
-
if (value instanceof Set) {
|
|
70207
|
-
if (markSeen(value, seen)) return "[Circular]";
|
|
70208
|
-
return Array.from(value.values(), (entry) => normalizeJsonValue(entry, seen));
|
|
70209
|
-
}
|
|
70210
|
-
if (!isPlainObject$1(value)) return String(value);
|
|
70211
|
-
if (markSeen(value, seen)) return "[Circular]";
|
|
70212
|
-
return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [key, normalizeJsonValue(entryValue, seen)]));
|
|
70213
|
-
}
|
|
70214
|
-
function compactTraceAttributes(attributes) {
|
|
70215
|
-
return Object.fromEntries(Object.entries(attributes).filter(([, value]) => value !== void 0).map(([key, value]) => [key, normalizeJsonValue(value)]));
|
|
70216
|
-
}
|
|
70217
|
-
function compactMetricAttributes(attributes) {
|
|
70218
|
-
return Object.fromEntries(Object.entries(attributes).flatMap(([key, value]) => {
|
|
70219
|
-
if (value === void 0 || value === null) return [];
|
|
70220
|
-
if (typeof value === "string") return [[key, value]];
|
|
70221
|
-
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return [[key, String(value)]];
|
|
70222
|
-
return [];
|
|
70223
|
-
}));
|
|
70224
|
-
}
|
|
70225
|
-
function outcomeFromExit(exit) {
|
|
70226
|
-
if (require_Schema$1.isSuccess(exit)) return "success";
|
|
70227
|
-
return require_Schema$1.hasInterruptsOnly(exit.cause) ? "interrupt" : "failure";
|
|
70228
|
-
}
|
|
70229
|
-
function normalizeModelMetricLabel(model) {
|
|
70230
|
-
const normalized = model?.trim().toLowerCase();
|
|
70231
|
-
if (!normalized) return;
|
|
70232
|
-
if (normalized.includes("gpt")) return "gpt";
|
|
70233
|
-
if (normalized.includes("claude")) return "claude";
|
|
70234
|
-
if (normalized.includes("gemini")) return "gemini";
|
|
70235
|
-
return "other";
|
|
70236
|
-
}
|
|
70237
|
-
//#endregion
|
|
70238
70406
|
//#region src/observability/TraceRecord.ts
|
|
70239
70407
|
function formatTraceExit(exit) {
|
|
70240
70408
|
if (require_Schema$1.isSuccess(exit)) return { _tag: "Success" };
|
|
@@ -71297,6 +71465,22 @@ async function readOAuthErrorCode(response) {
|
|
|
71297
71465
|
//#endregion
|
|
71298
71466
|
//#region src/command/cmmdRotationFence.ts
|
|
71299
71467
|
/**
|
|
71468
|
+
* Log and count a fence application, without changing the verdict it reports.
|
|
71469
|
+
*
|
|
71470
|
+
* The two facts named in the log line are exactly the ones the predicate
|
|
71471
|
+
* above the caller already computed — whether this session ever deposited a
|
|
71472
|
+
* refresh token of its own, and (only when that was false) whether the
|
|
71473
|
+
* session row reads as a desktop bearer session. Neither fact nor the log
|
|
71474
|
+
* line changes what the caller returns.
|
|
71475
|
+
*/
|
|
71476
|
+
function logRotationFenceApplied(input) {
|
|
71477
|
+
return require_Schema$1.logInfo("CMMD rotation fence applied: session may not rotate its own credential.", {
|
|
71478
|
+
sessionId: input.sessionId,
|
|
71479
|
+
hasStoredRefreshToken: input.hasStoredRefreshToken,
|
|
71480
|
+
isDesktopBearerSession: input.isDesktopBearerSession
|
|
71481
|
+
}).pipe(require_Schema$1.andThen(() => require_Schema$1.update(require_Schema$1.withAttributes(cmmdRotationFenceAppliedTotal, metricAttributes({})), 1)));
|
|
71482
|
+
}
|
|
71483
|
+
/**
|
|
71300
71484
|
* True when Forge must not rotate this session's CMMD credentials.
|
|
71301
71485
|
*
|
|
71302
71486
|
* Fails closed: if the session row cannot be read, assume the fence applies.
|
|
@@ -71304,8 +71488,22 @@ async function readOAuthErrorCode(response) {
|
|
|
71304
71488
|
* reconnect.
|
|
71305
71489
|
*/
|
|
71306
71490
|
function isRotationFencedSession(sessionId) {
|
|
71307
|
-
|
|
71308
|
-
|
|
71491
|
+
const hasStoredRefreshToken = readCommandRefreshTokenForSession(sessionId) !== null;
|
|
71492
|
+
if (hasStoredRefreshToken) return require_Schema$1.succeed(false);
|
|
71493
|
+
return isDesktopBearerAuthSession(sessionId).pipe(require_Schema$1.match({
|
|
71494
|
+
onFailure: () => ({
|
|
71495
|
+
fenced: true,
|
|
71496
|
+
isDesktopBearerSession: null
|
|
71497
|
+
}),
|
|
71498
|
+
onSuccess: (isDesktopBearerSession) => ({
|
|
71499
|
+
fenced: isDesktopBearerSession,
|
|
71500
|
+
isDesktopBearerSession
|
|
71501
|
+
})
|
|
71502
|
+
}), require_Schema$1.tap(({ fenced, isDesktopBearerSession }) => fenced ? logRotationFenceApplied({
|
|
71503
|
+
sessionId,
|
|
71504
|
+
hasStoredRefreshToken,
|
|
71505
|
+
isDesktopBearerSession
|
|
71506
|
+
}) : require_Schema$1.void_), require_Schema$1.map(({ fenced }) => fenced));
|
|
71309
71507
|
}
|
|
71310
71508
|
//#endregion
|
|
71311
71509
|
//#region src/cmmdConnection/cmmdCredentialRejection.ts
|
|
@@ -71342,7 +71540,7 @@ function forgetCmmdCredentialRejection(sessionId) {
|
|
|
71342
71540
|
*/
|
|
71343
71541
|
const clearCmmdAuthAfterCmmdRejection = (sessionId) => require_Schema$1.gen(function* () {
|
|
71344
71542
|
if (!hasCmmdCredentialRejection(sessionId)) return;
|
|
71345
|
-
yield* clearUnrecoverableCmmdAuthForSession(sessionId);
|
|
71543
|
+
yield* clearUnrecoverableCmmdAuthForSession(sessionId, "cmmd-rejected-credential");
|
|
71346
71544
|
forgetCmmdCredentialRejection(sessionId);
|
|
71347
71545
|
});
|
|
71348
71546
|
//#endregion
|
|
@@ -71353,6 +71551,23 @@ function usableCmmdUserJwt(token) {
|
|
|
71353
71551
|
function storeResolvedJwt(sessionId, accessToken, refreshToken) {
|
|
71354
71552
|
return setCommandJwtForSession(sessionId, accessToken, refreshToken ? { refreshToken } : {}).pipe(require_Schema$1.ignore({ log: true }));
|
|
71355
71553
|
}
|
|
71554
|
+
function logCmmdJwtRung(context, rung, outcome, reason) {
|
|
71555
|
+
return (outcome === "failed" ? require_Schema$1.logWarning : require_Schema$1.logInfo)("CMMD JWT resolver rung.", {
|
|
71556
|
+
sessionId: context.sessionId,
|
|
71557
|
+
subject: context.subject,
|
|
71558
|
+
deviceType: context.deviceType,
|
|
71559
|
+
caller: context.caller,
|
|
71560
|
+
rung,
|
|
71561
|
+
outcome,
|
|
71562
|
+
reason
|
|
71563
|
+
});
|
|
71564
|
+
}
|
|
71565
|
+
function recordCmmdCredentialRefreshMetric(rung, outcome) {
|
|
71566
|
+
return require_Schema$1.update(require_Schema$1.withAttributes(cmmdCredentialRefreshTotal, metricAttributes({
|
|
71567
|
+
rung,
|
|
71568
|
+
outcome
|
|
71569
|
+
})), 1);
|
|
71570
|
+
}
|
|
71356
71571
|
const inFlightRefreshes = /* @__PURE__ */ new Map();
|
|
71357
71572
|
function refreshOnceForSession(sessionId, refreshToken) {
|
|
71358
71573
|
return require_Schema$1.promise(() => {
|
|
@@ -71374,15 +71589,21 @@ function refreshOnceForSession(sessionId, refreshToken) {
|
|
|
71374
71589
|
* sibling session's token silently invalidates that sibling's stored copy and
|
|
71375
71590
|
* signs it out.
|
|
71376
71591
|
*/
|
|
71377
|
-
function trySelfRefresh(sessionId,
|
|
71592
|
+
function trySelfRefresh(sessionId, context) {
|
|
71378
71593
|
const currentRefreshToken = readCommandRefreshTokenForSession(sessionId);
|
|
71379
|
-
if (!currentRefreshToken) return require_Schema$1.
|
|
71594
|
+
if (!currentRefreshToken) return logCmmdJwtRung(context, "self-refresh", "skipped", "no refresh token stored for this session").pipe(require_Schema$1.as(null));
|
|
71380
71595
|
return require_Schema$1.gen(function* () {
|
|
71596
|
+
yield* logCmmdJwtRung(context, "self-refresh", "attempted", "rotating this session's own stored refresh token");
|
|
71597
|
+
yield* recordCmmdCredentialRefreshMetric("self-refresh", "attempted");
|
|
71381
71598
|
const refreshed = yield* refreshOnceForSession(sessionId, currentRefreshToken);
|
|
71382
71599
|
const accessToken = usableCmmdUserJwt(refreshed?.accessToken);
|
|
71383
|
-
if (!accessToken)
|
|
71600
|
+
if (!accessToken) {
|
|
71601
|
+
yield* logCmmdJwtRung(context, "self-refresh", "failed", "CMMD refresh did not return a usable user access JWT");
|
|
71602
|
+
return null;
|
|
71603
|
+
}
|
|
71384
71604
|
yield* storeResolvedJwt(sessionId, accessToken, refreshed?.refreshToken);
|
|
71385
|
-
|
|
71605
|
+
yield* recordCmmdCredentialRefreshMetric("self-refresh", "succeeded");
|
|
71606
|
+
yield* logCmmdJwtRung(context, "self-refresh", "succeeded", "CMMD rotated the stored refresh token");
|
|
71386
71607
|
return accessToken;
|
|
71387
71608
|
});
|
|
71388
71609
|
}
|
|
@@ -71399,16 +71620,22 @@ function trySelfRefresh(sessionId, logPrefix) {
|
|
|
71399
71620
|
* CMMD owns its lifetime, and a transient network failure must not throw away
|
|
71400
71621
|
* the one credential that can still recover the session.
|
|
71401
71622
|
*/
|
|
71402
|
-
function tryGrantRedemption(sessionId,
|
|
71623
|
+
function tryGrantRedemption(sessionId, context) {
|
|
71403
71624
|
const grantToken = readCommandSessionGrantForSession(sessionId);
|
|
71404
|
-
if (!grantToken) return require_Schema$1.
|
|
71625
|
+
if (!grantToken) return logCmmdJwtRung(context, "grant-redemption", "skipped", "no session grant stored for this session").pipe(require_Schema$1.as(null));
|
|
71405
71626
|
return require_Schema$1.gen(function* () {
|
|
71627
|
+
yield* logCmmdJwtRung(context, "grant-redemption", "attempted", "redeeming this session's durable CMMD session grant");
|
|
71628
|
+
yield* recordCmmdCredentialRefreshMetric("grant-redemption", "attempted");
|
|
71406
71629
|
const redeemed = yield* require_Schema$1.promise(() => redeemCmmdSessionGrant(grantToken)).pipe(require_Schema$1.orElseSucceed(() => null));
|
|
71407
71630
|
const accessToken = usableCmmdUserJwt(redeemed?.accessToken);
|
|
71408
|
-
if (!accessToken)
|
|
71631
|
+
if (!accessToken) {
|
|
71632
|
+
yield* logCmmdJwtRung(context, "grant-redemption", "failed", "session grant redemption did not return a usable user access JWT");
|
|
71633
|
+
return null;
|
|
71634
|
+
}
|
|
71409
71635
|
if (redeemed?.mcpToken) yield* setCommandAccessTokenForSession(sessionId, redeemed.mcpToken).pipe(require_Schema$1.ignore({ log: true }));
|
|
71410
71636
|
yield* storeResolvedJwt(sessionId, accessToken, redeemed?.refreshToken);
|
|
71411
|
-
|
|
71637
|
+
yield* recordCmmdCredentialRefreshMetric("grant-redemption", "succeeded");
|
|
71638
|
+
yield* logCmmdJwtRung(context, "grant-redemption", "succeeded", "session grant redeemed");
|
|
71412
71639
|
return accessToken;
|
|
71413
71640
|
});
|
|
71414
71641
|
}
|
|
@@ -71419,23 +71646,28 @@ function tryGrantRedemption(sessionId, logPrefix) {
|
|
|
71419
71646
|
* fallback for when that rotation is already gone. Neither touches another
|
|
71420
71647
|
* session's credentials, so this ladder is safe to run from any path.
|
|
71421
71648
|
*/
|
|
71422
|
-
function tryOwnCredentialRecovery(sessionId,
|
|
71649
|
+
function tryOwnCredentialRecovery(sessionId, context) {
|
|
71423
71650
|
return require_Schema$1.gen(function* () {
|
|
71424
|
-
const refreshed = yield* trySelfRefresh(sessionId,
|
|
71651
|
+
const refreshed = yield* trySelfRefresh(sessionId, context);
|
|
71425
71652
|
if (refreshed) return refreshed;
|
|
71426
|
-
return yield* tryGrantRedemption(sessionId,
|
|
71653
|
+
return yield* tryGrantRedemption(sessionId, context);
|
|
71427
71654
|
});
|
|
71428
71655
|
}
|
|
71429
|
-
function tryPromoteBootstrap(sessionId, subject, forceRefresh) {
|
|
71430
|
-
if (subject !== "owner-bootstrap" && subject !== "desktop-bootstrap") return require_Schema$1.
|
|
71656
|
+
function tryPromoteBootstrap(sessionId, subject, forceRefresh, context) {
|
|
71657
|
+
if (subject !== "owner-bootstrap" && subject !== "desktop-bootstrap") return logCmmdJwtRung(context, "promote-bootstrap", "skipped", "subject is not a bootstrap subject").pipe(require_Schema$1.as(null));
|
|
71431
71658
|
return require_Schema$1.gen(function* () {
|
|
71659
|
+
yield* logCmmdJwtRung(context, "promote-bootstrap", "attempted", "promoting the most recent owner CMMD auth to this bootstrap session");
|
|
71432
71660
|
yield* promoteMostRecentOwnerCommandAuthToSession(sessionId).pipe(require_Schema$1.catch_((error) => require_Schema$1.logWarning("Failed to promote owner CMMD auth for REST JWT resolution.", {
|
|
71433
71661
|
cause: error,
|
|
71434
71662
|
sessionId,
|
|
71435
71663
|
subject
|
|
71436
71664
|
})));
|
|
71437
71665
|
const promotedJwt = usableCmmdUserJwt(readCommandJwtForSession(sessionId));
|
|
71438
|
-
if (!forceRefresh && promotedJwt && !isJwtExpiredForSession(sessionId))
|
|
71666
|
+
if (!forceRefresh && promotedJwt && !isJwtExpiredForSession(sessionId)) {
|
|
71667
|
+
yield* logCmmdJwtRung(context, "promote-bootstrap", "succeeded", "promoted owner JWT is usable");
|
|
71668
|
+
return promotedJwt;
|
|
71669
|
+
}
|
|
71670
|
+
yield* logCmmdJwtRung(context, "promote-bootstrap", "failed", forceRefresh ? "forced refresh: a promoted JWT is never reused as-is" : "no promoted JWT available, or it is already expired");
|
|
71439
71671
|
return null;
|
|
71440
71672
|
});
|
|
71441
71673
|
}
|
|
@@ -71448,17 +71680,50 @@ function tryPromoteBootstrap(sessionId, subject, forceRefresh) {
|
|
|
71448
71680
|
* must stay private to the session it was issued to (see `trySelfRefresh`).
|
|
71449
71681
|
* Copying it is what let one expired tab sign the user out everywhere.
|
|
71450
71682
|
*/
|
|
71451
|
-
function trySubjectFallback(sessionId, subject,
|
|
71683
|
+
function trySubjectFallback(sessionId, subject, context) {
|
|
71452
71684
|
return require_Schema$1.gen(function* () {
|
|
71453
|
-
|
|
71454
|
-
const fallbackJwt = usableCmmdUserJwt(
|
|
71455
|
-
if (!fallbackJwt)
|
|
71685
|
+
yield* logCmmdJwtRung(context, "subject-fallback", "attempted", "looking for a sibling session's still-valid JWT for this subject");
|
|
71686
|
+
const fallbackJwt = usableCmmdUserJwt((yield* readMostRecentJwtForSubject(subject).pipe(require_Schema$1.orElseSucceed(() => null)))?.jwt);
|
|
71687
|
+
if (!fallbackJwt) {
|
|
71688
|
+
yield* logCmmdJwtRung(context, "subject-fallback", "failed", "no sibling session holds a still-valid JWT for this subject");
|
|
71689
|
+
return null;
|
|
71690
|
+
}
|
|
71456
71691
|
yield* storeResolvedJwt(sessionId, fallbackJwt, null);
|
|
71457
|
-
|
|
71692
|
+
yield* logCmmdJwtRung(context, "subject-fallback", "succeeded", "borrowed a sibling session's still-valid JWT");
|
|
71458
71693
|
return fallbackJwt;
|
|
71459
71694
|
});
|
|
71460
71695
|
}
|
|
71461
71696
|
/**
|
|
71697
|
+
* The rungs below the rotation fence: forced own-credential recovery,
|
|
71698
|
+
* bootstrap promotion, subject fallback, then own-credential recovery again
|
|
71699
|
+
* as the last resort. Split out of `resolveCmmdJwtForSession` so each half of
|
|
71700
|
+
* the ladder stays independently readable (and independently testable) —
|
|
71701
|
+
* the fence check above is the one decision the split does not touch.
|
|
71702
|
+
*/
|
|
71703
|
+
function runRecoveryLadder(sessionId, normalizedSubject, forceRefresh, context) {
|
|
71704
|
+
return require_Schema$1.gen(function* () {
|
|
71705
|
+
if (forceRefresh) {
|
|
71706
|
+
const forced = yield* tryOwnCredentialRecovery(sessionId, context);
|
|
71707
|
+
if (forced) return forced;
|
|
71708
|
+
}
|
|
71709
|
+
if (normalizedSubject) {
|
|
71710
|
+
const promoted = yield* tryPromoteBootstrap(sessionId, normalizedSubject, forceRefresh, context);
|
|
71711
|
+
if (promoted) return promoted;
|
|
71712
|
+
}
|
|
71713
|
+
if (!normalizedSubject) {
|
|
71714
|
+
yield* logCmmdJwtRung(context, "cache", "failed", "no subject to resolve and forced recovery already exhausted");
|
|
71715
|
+
return null;
|
|
71716
|
+
}
|
|
71717
|
+
if (!forceRefresh) {
|
|
71718
|
+
const fallback = yield* trySubjectFallback(sessionId, normalizedSubject, context);
|
|
71719
|
+
if (fallback) return fallback;
|
|
71720
|
+
}
|
|
71721
|
+
const recovered = yield* tryOwnCredentialRecovery(sessionId, context);
|
|
71722
|
+
if (!recovered) yield* logCmmdJwtRung(context, "cache", "failed", "recovery ladder exhausted: no rung produced a usable JWT");
|
|
71723
|
+
return recovered;
|
|
71724
|
+
});
|
|
71725
|
+
}
|
|
71726
|
+
/**
|
|
71462
71727
|
* Resolve the CMMD REST JWT for a Forge session.
|
|
71463
71728
|
*
|
|
71464
71729
|
* Forge session auth is long-lived compared with the short-lived CMMD JWT.
|
|
@@ -71472,26 +71737,30 @@ function resolveCmmdJwtForSession({ sessionId, subject, logPrefix = "cmmd-auth",
|
|
|
71472
71737
|
const cached = usableCmmdUserJwt(readCommandJwtForSession(sessionId));
|
|
71473
71738
|
if (cached && !isJwtExpiredForSession(sessionId)) {
|
|
71474
71739
|
if (!forceRefresh) return require_Schema$1.succeed(cached);
|
|
71475
|
-
if (staleAccessToken && cached !== staleAccessToken) return require_Schema$1.succeed(cached)
|
|
71740
|
+
if (staleAccessToken && cached !== staleAccessToken) return require_Schema$1.succeed(cached).pipe(require_Schema$1.tap(() => require_Schema$1.gen(function* () {
|
|
71741
|
+
const deviceType = yield* readClientDeviceTypeForSession(sessionId);
|
|
71742
|
+
yield* logCmmdJwtRung({
|
|
71743
|
+
sessionId,
|
|
71744
|
+
subject: subject?.trim() || null,
|
|
71745
|
+
deviceType,
|
|
71746
|
+
caller: logPrefix
|
|
71747
|
+
}, "cache", "skipped", "forced refresh skipped: cache already holds a token another caller rotated");
|
|
71748
|
+
})));
|
|
71476
71749
|
}
|
|
71477
71750
|
const normalizedSubject = subject?.trim() || null;
|
|
71478
71751
|
if (!normalizedSubject && !forceRefresh) return require_Schema$1.succeed(null);
|
|
71479
71752
|
return require_Schema$1.gen(function* () {
|
|
71480
|
-
|
|
71481
|
-
|
|
71482
|
-
|
|
71483
|
-
|
|
71484
|
-
|
|
71485
|
-
|
|
71486
|
-
|
|
71487
|
-
|
|
71488
|
-
|
|
71489
|
-
if (!normalizedSubject) return null;
|
|
71490
|
-
if (!forceRefresh) {
|
|
71491
|
-
const fallback = yield* trySubjectFallback(sessionId, normalizedSubject, logPrefix);
|
|
71492
|
-
if (fallback) return fallback;
|
|
71753
|
+
const context = {
|
|
71754
|
+
sessionId,
|
|
71755
|
+
subject: normalizedSubject,
|
|
71756
|
+
deviceType: yield* readClientDeviceTypeForSession(sessionId),
|
|
71757
|
+
caller: logPrefix
|
|
71758
|
+
};
|
|
71759
|
+
if (yield* isRotationFencedSession(sessionId)) {
|
|
71760
|
+
yield* logCmmdJwtRung(context, "rotation-fence", "failed", "session is fenced from rotating CMMD credentials");
|
|
71761
|
+
return null;
|
|
71493
71762
|
}
|
|
71494
|
-
return yield*
|
|
71763
|
+
return yield* runRecoveryLadder(sessionId, normalizedSubject, forceRefresh, context);
|
|
71495
71764
|
});
|
|
71496
71765
|
}
|
|
71497
71766
|
//#endregion
|
|
@@ -72228,9 +72497,14 @@ function buildProxyErrorResponse(error) {
|
|
|
72228
72497
|
* warning rather than failing the request if the clear itself errors — a
|
|
72229
72498
|
* session that already has no usable credential must still get its 401,
|
|
72230
72499
|
* not a 500 from a failed best-effort cleanup.
|
|
72500
|
+
*
|
|
72501
|
+
* Reached only after CMMD itself rejected the request with both the original
|
|
72502
|
+
* and (if retried) the refreshed JWT, so — unlike `respondCmmdConnectionRequired`
|
|
72503
|
+
* below — this always deletes the row. `clearUnrecoverableCmmdAuthForSession`
|
|
72504
|
+
* logs the row count and the reason itself; this only names the reason.
|
|
72231
72505
|
*/
|
|
72232
72506
|
function clearCmmdAuthAfterFailure(sessionId) {
|
|
72233
|
-
return clearUnrecoverableCmmdAuthForSession(sessionId).pipe(require_Schema$1.catch_((error) => require_Schema$1.logWarning("Failed to clear unrecoverable CMMD auth after CMMD auth failure.", {
|
|
72507
|
+
return clearUnrecoverableCmmdAuthForSession(sessionId, "cmmd-proxy-auth-error").pipe(require_Schema$1.catch_((error) => require_Schema$1.logWarning("Failed to clear unrecoverable CMMD auth after CMMD auth failure.", {
|
|
72234
72508
|
cause: error,
|
|
72235
72509
|
sessionId
|
|
72236
72510
|
})));
|
|
@@ -72251,7 +72525,7 @@ function clearCmmdAuthAfterFailure(sessionId) {
|
|
|
72251
72525
|
* refused the credential, which `clearCmmdAuthAfterCmmdRejection` checks.
|
|
72252
72526
|
*/
|
|
72253
72527
|
function respondCmmdConnectionRequired(sessionId) {
|
|
72254
|
-
return
|
|
72528
|
+
return require_Schema$1.logInfo("CMMD proxy request answered CMMD_AUTH_REQUIRED.", { sessionId }).pipe(require_Schema$1.andThen(() => clearCmmdAuthAfterCmmdRejection(sessionId)), require_Schema$1.catch_((error) => require_Schema$1.logWarning("Failed to clear CMMD auth that CMMD itself rejected.", {
|
|
72255
72529
|
cause: error,
|
|
72256
72530
|
sessionId
|
|
72257
72531
|
})), require_Schema$1.as(buildProxyErrorResponse(new CmmdProxyAuthenticationRequiredError("CMMD connection required. Reconnect CMMD to continue."))));
|
|
@@ -95034,60 +95308,6 @@ const dispatchBootstrapTurnStart = (input, command) => require_Schema$1.gen(func
|
|
|
95034
95308
|
*/
|
|
95035
95309
|
var OrchestrationEngineService = class extends require_Schema$1.Service()("forge/orchestration/Services/OrchestrationEngine/OrchestrationEngineService") {};
|
|
95036
95310
|
//#endregion
|
|
95037
|
-
//#region src/observability/Metrics.ts
|
|
95038
|
-
const rpcRequestsTotal = require_Schema$1.counter("t3_rpc_requests_total", { description: "Total RPC requests handled by the websocket RPC server." });
|
|
95039
|
-
const rpcRequestDuration = require_Schema$1.timer("t3_rpc_request_duration", { description: "RPC request handling duration." });
|
|
95040
|
-
const orchestrationCommandsTotal = require_Schema$1.counter("t3_orchestration_commands_total", { description: "Total orchestration commands dispatched." });
|
|
95041
|
-
const orchestrationCommandDuration = require_Schema$1.timer("t3_orchestration_command_duration", { description: "Orchestration command dispatch duration." });
|
|
95042
|
-
const orchestrationCommandAckDuration = require_Schema$1.timer("t3_orchestration_command_ack_duration", { description: "Time from orchestration command dispatch to the first committed domain event emitted for that command." });
|
|
95043
|
-
const orchestrationEventsProcessedTotal = require_Schema$1.counter("t3_orchestration_events_processed_total", { description: "Total orchestration intent events processed by runtime reactors." });
|
|
95044
|
-
const providerSessionsTotal = require_Schema$1.counter("t3_provider_sessions_total", { description: "Total provider session lifecycle operations." });
|
|
95045
|
-
const providerTurnsTotal = require_Schema$1.counter("t3_provider_turns_total", { description: "Total provider turn lifecycle operations." });
|
|
95046
|
-
const providerTurnDuration = require_Schema$1.timer("t3_provider_turn_duration", { description: "Provider turn request duration." });
|
|
95047
|
-
const providerRuntimeEventsTotal = require_Schema$1.counter("t3_provider_runtime_events_total", { description: "Total canonical provider runtime events processed." });
|
|
95048
|
-
const gitCommandsTotal = require_Schema$1.counter("t3_git_commands_total", { description: "Total git commands executed by the server runtime." });
|
|
95049
|
-
const gitCommandDuration = require_Schema$1.timer("t3_git_command_duration", { description: "Git command execution duration." });
|
|
95050
|
-
const terminalSessionsTotal = require_Schema$1.counter("t3_terminal_sessions_total", { description: "Total terminal sessions started." });
|
|
95051
|
-
const browserSessionsReapedTotal = require_Schema$1.counter("browser_sessions_reaped_total", { description: "Browser sessions and Chromium processes reclaimed by the orphan reaper, by reason (orphan | ttl | stale_profile)." });
|
|
95052
|
-
const terminalRestartsTotal = require_Schema$1.counter("t3_terminal_restarts_total", { description: "Total terminal restart requests handled." });
|
|
95053
|
-
const runtimeReplicationOutboxDepth = require_Schema$1.gauge("t3_runtime_replication_outbox_depth", { description: "Current runtime replication outbox items by durable state." });
|
|
95054
|
-
const runtimeReplicationOutboxOldestEventAge = require_Schema$1.gauge("t3_runtime_replication_outbox_oldest_event_age_seconds", { description: "Age in seconds of the oldest undelivered runtime replication payload." });
|
|
95055
|
-
const runtimeReplicationPublicationAttemptsTotal = require_Schema$1.counter("t3_runtime_replication_publication_attempts_total", { description: "Total canonical runtime replication publication attempts." });
|
|
95056
|
-
const runtimeReplicationPublicationRetriesTotal = require_Schema$1.counter("t3_runtime_replication_publication_retries_total", { description: "Total runtime replication retries scheduled after recoverable failures." });
|
|
95057
|
-
const runtimeReplicationPublicationLatency = require_Schema$1.timer("t3_runtime_replication_publication_latency", { description: "Time from durable enqueue to canonical CMMD acknowledgement." });
|
|
95058
|
-
const metricAttributes = (attributes) => Object.entries(compactMetricAttributes(attributes));
|
|
95059
|
-
const increment = (metric, attributes, amount = 1) => require_Schema$1.update(require_Schema$1.withAttributes(metric, metricAttributes(attributes)), amount);
|
|
95060
|
-
const withMetricsImpl = (effect, options) => require_Schema$1.gen(function* () {
|
|
95061
|
-
const startedAt = Date.now();
|
|
95062
|
-
const exit$2 = yield* require_Schema$1.exit(effect);
|
|
95063
|
-
const duration = require_Schema$1.millis(Math.max(0, Date.now() - startedAt));
|
|
95064
|
-
const baseAttributes = typeof options.attributes === "function" ? options.attributes() : options.attributes ?? {};
|
|
95065
|
-
if (options.timer) yield* require_Schema$1.update(require_Schema$1.withAttributes(options.timer, metricAttributes(baseAttributes)), duration);
|
|
95066
|
-
if (options.counter) {
|
|
95067
|
-
const outcome = outcomeFromExit(exit$2);
|
|
95068
|
-
yield* require_Schema$1.update(require_Schema$1.withAttributes(options.counter, metricAttributes({
|
|
95069
|
-
...baseAttributes,
|
|
95070
|
-
outcome,
|
|
95071
|
-
...options.outcomeAttributes ? options.outcomeAttributes(outcome) : {}
|
|
95072
|
-
})), 1);
|
|
95073
|
-
}
|
|
95074
|
-
if (require_Schema$1.isSuccess(exit$2)) return exit$2.value;
|
|
95075
|
-
return yield* require_Schema$1.failCause(exit$2.cause);
|
|
95076
|
-
});
|
|
95077
|
-
const withMetrics = require_Schema$1.dual(2, withMetricsImpl);
|
|
95078
|
-
const providerMetricAttributes = (provider, extra) => compactMetricAttributes({
|
|
95079
|
-
provider,
|
|
95080
|
-
...extra
|
|
95081
|
-
});
|
|
95082
|
-
const providerTurnMetricAttributes = (input) => {
|
|
95083
|
-
const modelFamily = normalizeModelMetricLabel(input.model);
|
|
95084
|
-
return compactMetricAttributes({
|
|
95085
|
-
provider: input.provider,
|
|
95086
|
-
...modelFamily ? { modelFamily } : {},
|
|
95087
|
-
...input.extra
|
|
95088
|
-
});
|
|
95089
|
-
};
|
|
95090
|
-
//#endregion
|
|
95091
95311
|
//#region src/observability/RpcInstrumentation.ts
|
|
95092
95312
|
const annotateRpcSpan = (method, traceAttributes) => require_Schema$1.annotateCurrentSpan({
|
|
95093
95313
|
"rpc.method": method,
|
|
@@ -98299,6 +98519,28 @@ function serializeWorktreeLaunchActivity(activity) {
|
|
|
98299
98519
|
function decodeWorktreeLaunchActivityKind(kind) {
|
|
98300
98520
|
return isCmmdRuntimeWorktreeLaunchActivityKind(kind) ? kind : null;
|
|
98301
98521
|
}
|
|
98522
|
+
function planStepsOf(activity) {
|
|
98523
|
+
const decoded = decodeCmmdRuntimePlanActivity(encodeCmmdRuntimePlanActivityPayload(activity.kind, activity.payload));
|
|
98524
|
+
return decoded !== null && decoded.plan.length > 0 ? decoded.plan : null;
|
|
98525
|
+
}
|
|
98526
|
+
function serializePlanActivity(activity) {
|
|
98527
|
+
if (activity.kind !== "turn.plan.updated") return null;
|
|
98528
|
+
const plan = planStepsOf(activity);
|
|
98529
|
+
if (plan === null) return null;
|
|
98530
|
+
const completed = plan.filter((step) => step.status === "completed").length;
|
|
98531
|
+
return {
|
|
98532
|
+
sourceSequence: null,
|
|
98533
|
+
event: {
|
|
98534
|
+
eventId: `activity:${activity.id}`,
|
|
98535
|
+
occurredAt: canonicalRuntimeEventTimestamp(activity.createdAt),
|
|
98536
|
+
...eventTurn(activity.turnId),
|
|
98537
|
+
kind: "tool_call",
|
|
98538
|
+
summary: `Plan: ${completed} of ${plan.length} steps done`,
|
|
98539
|
+
status: "completed",
|
|
98540
|
+
payload: encodeCmmdRuntimePlanActivityPayload(activity.kind, activity.payload)
|
|
98541
|
+
}
|
|
98542
|
+
};
|
|
98543
|
+
}
|
|
98302
98544
|
function isReplicatedFailureActivity(activity) {
|
|
98303
98545
|
return activity.kind === "runtime.error" || activity.kind.startsWith("provider.") && activity.kind.endsWith(".failed");
|
|
98304
98546
|
}
|
|
@@ -98683,6 +98925,10 @@ function serializeRuntimeThreadEvents(thread) {
|
|
|
98683
98925
|
const event = serializeWorktreeLaunchActivity(activity);
|
|
98684
98926
|
return event === null ? [] : [event];
|
|
98685
98927
|
});
|
|
98928
|
+
const planActivities = thread.activities.flatMap((activity) => {
|
|
98929
|
+
const event = serializePlanActivity(activity);
|
|
98930
|
+
return event === null ? [] : [event];
|
|
98931
|
+
});
|
|
98686
98932
|
const recordedTurns = serializeRecordedTurns(thread);
|
|
98687
98933
|
const textGenerations = serializeTextGenerations(thread);
|
|
98688
98934
|
return [
|
|
@@ -98690,6 +98936,7 @@ function serializeRuntimeThreadEvents(thread) {
|
|
|
98690
98936
|
...replicaActivities,
|
|
98691
98937
|
...toolActivities,
|
|
98692
98938
|
...worktreeLaunchActivities,
|
|
98939
|
+
...planActivities,
|
|
98693
98940
|
...failureActivities,
|
|
98694
98941
|
...recordedTurns,
|
|
98695
98942
|
...textGenerations
|
|
@@ -292807,7 +293054,7 @@ const make$8 = require_Schema$1.gen(function* () {
|
|
|
292807
293054
|
summary: "Provider turn start failed",
|
|
292808
293055
|
detail: `User message '${event.payload.messageId}' was not found for turn start request.`,
|
|
292809
293056
|
turnId: null,
|
|
292810
|
-
createdAt:
|
|
293057
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
292811
293058
|
});
|
|
292812
293059
|
return;
|
|
292813
293060
|
}
|
|
@@ -292860,7 +293107,7 @@ const make$8 = require_Schema$1.gen(function* () {
|
|
|
292860
293107
|
summary: "Provider turn start failed",
|
|
292861
293108
|
detail: publicProviderFailureDetail(require_Schema$1.pretty(cause)),
|
|
292862
293109
|
turnId: null,
|
|
292863
|
-
createdAt:
|
|
293110
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
292864
293111
|
})));
|
|
292865
293112
|
});
|
|
292866
293113
|
const processTurnInterruptRequested = require_Schema$1.fn("processTurnInterruptRequested")(function* (event) {
|
|
@@ -298543,7 +298790,7 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
298543
298790
|
* environment descriptor down instead of reporting an honest "unknown".
|
|
298544
298791
|
*/
|
|
298545
298792
|
function readBakedBuildCommit() {
|
|
298546
|
-
return "
|
|
298793
|
+
return "78b0e072680ed734c4eb8337a6d38c87e7e34393";
|
|
298547
298794
|
}
|
|
298548
298795
|
async function resolveServerBuildCommit(input) {
|
|
298549
298796
|
if (isFullCommitSha(input.baked)) return input.baked;
|
|
@@ -309967,6 +310214,33 @@ registerProxyRoute({
|
|
|
309967
310214
|
cmmdPath: "/api/projects/campaigns/:id",
|
|
309968
310215
|
orgIdSource: "none"
|
|
309969
310216
|
});
|
|
310217
|
+
/**
|
|
310218
|
+
* Apple App Store guideline 5.1.1(v): an app that supports account creation
|
|
310219
|
+
* or sign-in must let the user start account deletion from inside the app.
|
|
310220
|
+
* CMMD owns the account, so this proxies to its existing deactivation route.
|
|
310221
|
+
*
|
|
310222
|
+
* `cmmdPath` is `/api/settings/delete-account`, not a `/api/forge/context/*`
|
|
310223
|
+
* route: CMMD never built a forge-context-scoped deletion endpoint. It is
|
|
310224
|
+
* still reachable through the same owner JWT this gateway already resolves
|
|
310225
|
+
* per session: CMMD's `optionalJwtAuth` runs ahead of every `/api/*` route
|
|
310226
|
+
* (not only forge-context ones), so the same Bearer that reaches
|
|
310227
|
+
* `/api/cmmd/bootstrap` also authenticates here.
|
|
310228
|
+
*
|
|
310229
|
+
* CMMD's route soft-deletes: it flips the user to `active: false` and
|
|
310230
|
+
* `lifecycleState: 'deactivated'`, revokes CMMD's own refresh tokens, and
|
|
310231
|
+
* destroys its session. It does not purge data on this call. The Forge
|
|
310232
|
+
* client is responsible for the rest: revoking the Forge session
|
|
310233
|
+
* (`/api/auth/logout`) and clearing local state, the same as a manual
|
|
310234
|
+
* sign-out.
|
|
310235
|
+
*/
|
|
310236
|
+
registerProxyRoute({
|
|
310237
|
+
forgePath: "/api/cmmd/settings/delete-account",
|
|
310238
|
+
method: "DELETE",
|
|
310239
|
+
cmmdPath: "/api/settings/delete-account",
|
|
310240
|
+
orgIdSource: "none",
|
|
310241
|
+
responseShape: "unwrapData",
|
|
310242
|
+
requestBody: "none"
|
|
310243
|
+
});
|
|
309970
310244
|
registerProxyRoute({
|
|
309971
310245
|
forgePath: "/api/cmmd/bootstrap",
|
|
309972
310246
|
method: "GET",
|