@cmmd-center/forge 0.13.14 → 0.13.16
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 +655 -275
- package/dist/bin.mjs +654 -275
- package/dist/client/assets/{DiffPanel-P3t5Fq1q.js → DiffPanel-DI782ZMI.js} +3 -3
- package/dist/client/assets/{DiffPanel.logic-BXQ1C0-e.js → DiffPanel.logic-DtVm1IjE.js} +2 -2
- package/dist/client/assets/DiffPanelShell-IB1GWoYr.js +2533 -0
- package/dist/client/assets/{DiffWorkerPoolProvider-3_hkcEDq.js → DiffWorkerPoolProvider-DDMgJC0H.js} +9 -9
- package/dist/client/assets/PullRequestCodePanel-DPwb2trb.js +5 -0
- package/dist/client/assets/{Virtualizer-DCt36RGD.js → Virtualizer-CU-TBcmx.js} +2 -2
- package/dist/client/assets/index-B9000khV.css +1 -0
- package/dist/client/assets/index-CB-vPC0y.js +153 -0
- package/dist/client/index.html +5 -5
- package/dist/forge-build.json +1 -1
- package/dist/runtimeBootstrap.cjs +1 -1
- package/dist/runtimeBootstrap.mjs +1 -1
- package/dist/{runtimeControlAuthority-BkVB0uAA.cjs → runtimeControlAuthority-DCitZFTk.cjs} +3 -1
- package/dist/{runtimeControlAuthority-E_kdeD_P.mjs → runtimeControlAuthority-DpRIoWqP.mjs} +3 -1
- package/package.json +1 -1
- package/dist/client/assets/DiffPanelShell-3EpWEr7p.js +0 -2532
- package/dist/client/assets/PullRequestCodePanel-B2T6MPcE.js +0 -5
- package/dist/client/assets/index-C3pC5t1K.css +0 -1
- package/dist/client/assets/index-DvwXD11B.js +0 -154
package/dist/bin.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { a as make$51, i as keys, n as getOption, o as makeWith$1, r as invalida
|
|
|
11
11
|
import { S as layer$9, _ as defaultEscape, a as DeadlockError, b as makeCompiler$1, c as SqlError, d as UnknownError, g as custom, h as make$52, i as ConstraintError, l as SqlSyntaxError, m as SqlClient, n as AuthorizationError, o as LockTimeoutError, r as ConnectionError, s as SerializationError, t as AuthenticationError, u as StatementTimeoutError, v as defaultTransforms, y as fragment } from "./SqlError-BMBkezUO.mjs";
|
|
12
12
|
import { r as wrapper_default, t as Mime_default } from "./Mime-Dq9IoNiV.mjs";
|
|
13
13
|
import { a as diag, c as SpanKind$1, d as TraceFlags, f as ROOT_CONTEXT, i as propagation, l as isSpanContextValid, m as DiagLogLevel, n as init_esm$1, o as context$1, p as createContextKey, r as trace, s as SpanStatusCode, t as esm_exports$1 } from "./esm-wLIF-u3N.mjs";
|
|
14
|
-
import { a as verifyCmmdAuthRuntimeTicket, c as verifyCmmdRuntimeTicket, d as runtimeGrantToken, f as setRuntimeGrantToken, i as sendRuntimeControlHeartbeat, l as verifyCmmdRuntimeTicketRefreshProof, n as bootstrapRuntimeControlAuthorityFromNarrowGrant, o as verifyCmmdClaudeReadinessTicket, p as createReadOnceFileTokenAccessor, r as readRuntimeControlAuthority, s as verifyCmmdClaudeUsageTicket, t as bootstrapRuntimeControlAuthority, u as verifyCmmdScopedRuntimePayload } from "./runtimeControlAuthority-
|
|
14
|
+
import { a as verifyCmmdAuthRuntimeTicket, c as verifyCmmdRuntimeTicket, d as runtimeGrantToken, f as setRuntimeGrantToken, i as sendRuntimeControlHeartbeat, l as verifyCmmdRuntimeTicketRefreshProof, n as bootstrapRuntimeControlAuthorityFromNarrowGrant, o as verifyCmmdClaudeReadinessTicket, p as createReadOnceFileTokenAccessor, r as readRuntimeControlAuthority, s as verifyCmmdClaudeUsageTicket, t as bootstrapRuntimeControlAuthority, u as verifyCmmdScopedRuntimePayload } from "./runtimeControlAuthority-DpRIoWqP.mjs";
|
|
15
15
|
import { n as PtySpawnError, t as PtyAdapter } from "./PTY-BzdbZ0Ym.mjs";
|
|
16
16
|
import { createRequire } from "node:module";
|
|
17
17
|
import * as HE from "node:child_process";
|
|
@@ -26921,6 +26921,44 @@ function encodeCmmdRuntimeWorktreeLaunchActivityPayload(kind, payload) {
|
|
|
26921
26921
|
function isCmmdRuntimeWorktreeLaunchActivityKind(kind) {
|
|
26922
26922
|
return WORKTREE_LAUNCH_ACTIVITY_KINDS.has(kind);
|
|
26923
26923
|
}
|
|
26924
|
+
/**
|
|
26925
|
+
* A plan update rides the `tool_call` event kind because the forge-context
|
|
26926
|
+
* contract has no plan event kind, the same trade the launch rows make. The
|
|
26927
|
+
* steps are carried verbatim under a versioned marker so a cloud client can
|
|
26928
|
+
* rebuild the composer plan banner from the durable replica. (#3319)
|
|
26929
|
+
*/
|
|
26930
|
+
const CMMD_RUNTIME_PLAN_ACTIVITY_KIND = "turn.plan.updated";
|
|
26931
|
+
function encodeCmmdRuntimePlanActivityPayload(kind, payload) {
|
|
26932
|
+
return {
|
|
26933
|
+
...isRecord$23(payload) ? payload : { sourcePayload: payload },
|
|
26934
|
+
forgePlanActivity: {
|
|
26935
|
+
schemaVersion: 1,
|
|
26936
|
+
source: "native",
|
|
26937
|
+
kind
|
|
26938
|
+
}
|
|
26939
|
+
};
|
|
26940
|
+
}
|
|
26941
|
+
function decodeCmmdRuntimePlanActivity(payload) {
|
|
26942
|
+
if (!isRecord$23(payload)) return null;
|
|
26943
|
+
const metadata = payload["forgePlanActivity"];
|
|
26944
|
+
if (!isRecord$23(metadata)) return null;
|
|
26945
|
+
if (metadata["schemaVersion"] !== 1 || metadata["source"] !== "native" || metadata["kind"] !== "turn.plan.updated") return null;
|
|
26946
|
+
const rawPlan = payload["plan"];
|
|
26947
|
+
return {
|
|
26948
|
+
kind: CMMD_RUNTIME_PLAN_ACTIVITY_KIND,
|
|
26949
|
+
plan: Array.isArray(rawPlan) ? rawPlan.flatMap((entry) => {
|
|
26950
|
+
if (!isRecord$23(entry)) return [];
|
|
26951
|
+
const step = entry["step"];
|
|
26952
|
+
const status = entry["status"];
|
|
26953
|
+
if (typeof step !== "string" || step.trim().length === 0) return [];
|
|
26954
|
+
if (status !== "pending" && status !== "inProgress" && status !== "completed") return [];
|
|
26955
|
+
return [{
|
|
26956
|
+
step,
|
|
26957
|
+
status
|
|
26958
|
+
}];
|
|
26959
|
+
}) : []
|
|
26960
|
+
};
|
|
26961
|
+
}
|
|
26924
26962
|
//#endregion
|
|
26925
26963
|
//#region ../../packages/contracts/src/cmmdProjectPatch.ts
|
|
26926
26964
|
const CMMD_PROJECT_PATCH_FIELDS = {
|
|
@@ -66085,7 +66123,7 @@ function normalizeNumberish(value) {
|
|
|
66085
66123
|
}
|
|
66086
66124
|
//#endregion
|
|
66087
66125
|
//#region package.json
|
|
66088
|
-
var version$1 = "0.13.
|
|
66126
|
+
var version$1 = "0.13.16";
|
|
66089
66127
|
//#endregion
|
|
66090
66128
|
//#region src/sentry.ts
|
|
66091
66129
|
const SERVER_APP_NAME = "forge-server";
|
|
@@ -66965,6 +67003,121 @@ function verifyCmmdForgeCloudOwnerAssertion(token, publicKeyPem, expectedKeyId,
|
|
|
66965
67003
|
};
|
|
66966
67004
|
}
|
|
66967
67005
|
//#endregion
|
|
67006
|
+
//#region src/observability/Attributes.ts
|
|
67007
|
+
function isPlainObject$1(value) {
|
|
67008
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
67009
|
+
}
|
|
67010
|
+
function markSeen(value, seen) {
|
|
67011
|
+
if (seen.has(value)) return true;
|
|
67012
|
+
seen.add(value);
|
|
67013
|
+
return false;
|
|
67014
|
+
}
|
|
67015
|
+
function normalizeJsonValue(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
67016
|
+
if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value ?? null;
|
|
67017
|
+
if (typeof value === "bigint") return value.toString();
|
|
67018
|
+
if (value instanceof Date) return Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString();
|
|
67019
|
+
if (value instanceof Error) return {
|
|
67020
|
+
name: value.name,
|
|
67021
|
+
message: value.message,
|
|
67022
|
+
...value.stack ? { stack: value.stack } : {}
|
|
67023
|
+
};
|
|
67024
|
+
if (Array.isArray(value)) {
|
|
67025
|
+
if (markSeen(value, seen)) return "[Circular]";
|
|
67026
|
+
return value.map((entry) => normalizeJsonValue(entry, seen));
|
|
67027
|
+
}
|
|
67028
|
+
if (value instanceof Map) {
|
|
67029
|
+
if (markSeen(value, seen)) return "[Circular]";
|
|
67030
|
+
return Object.fromEntries(Array.from(value.entries(), ([key, entryValue]) => [String(key), normalizeJsonValue(entryValue, seen)]));
|
|
67031
|
+
}
|
|
67032
|
+
if (value instanceof Set) {
|
|
67033
|
+
if (markSeen(value, seen)) return "[Circular]";
|
|
67034
|
+
return Array.from(value.values(), (entry) => normalizeJsonValue(entry, seen));
|
|
67035
|
+
}
|
|
67036
|
+
if (!isPlainObject$1(value)) return String(value);
|
|
67037
|
+
if (markSeen(value, seen)) return "[Circular]";
|
|
67038
|
+
return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [key, normalizeJsonValue(entryValue, seen)]));
|
|
67039
|
+
}
|
|
67040
|
+
function compactTraceAttributes(attributes) {
|
|
67041
|
+
return Object.fromEntries(Object.entries(attributes).filter(([, value]) => value !== void 0).map(([key, value]) => [key, normalizeJsonValue(value)]));
|
|
67042
|
+
}
|
|
67043
|
+
function compactMetricAttributes(attributes) {
|
|
67044
|
+
return Object.fromEntries(Object.entries(attributes).flatMap(([key, value]) => {
|
|
67045
|
+
if (value === void 0 || value === null) return [];
|
|
67046
|
+
if (typeof value === "string") return [[key, value]];
|
|
67047
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return [[key, String(value)]];
|
|
67048
|
+
return [];
|
|
67049
|
+
}));
|
|
67050
|
+
}
|
|
67051
|
+
function outcomeFromExit(exit) {
|
|
67052
|
+
if (isSuccess(exit)) return "success";
|
|
67053
|
+
return hasInterruptsOnly(exit.cause) ? "interrupt" : "failure";
|
|
67054
|
+
}
|
|
67055
|
+
function normalizeModelMetricLabel(model) {
|
|
67056
|
+
const normalized = model?.trim().toLowerCase();
|
|
67057
|
+
if (!normalized) return;
|
|
67058
|
+
if (normalized.includes("gpt")) return "gpt";
|
|
67059
|
+
if (normalized.includes("claude")) return "claude";
|
|
67060
|
+
if (normalized.includes("gemini")) return "gemini";
|
|
67061
|
+
return "other";
|
|
67062
|
+
}
|
|
67063
|
+
//#endregion
|
|
67064
|
+
//#region src/observability/Metrics.ts
|
|
67065
|
+
const rpcRequestsTotal = counter("t3_rpc_requests_total", { description: "Total RPC requests handled by the websocket RPC server." });
|
|
67066
|
+
const rpcRequestDuration = timer("t3_rpc_request_duration", { description: "RPC request handling duration." });
|
|
67067
|
+
const orchestrationCommandsTotal = counter("t3_orchestration_commands_total", { description: "Total orchestration commands dispatched." });
|
|
67068
|
+
const orchestrationCommandDuration = timer("t3_orchestration_command_duration", { description: "Orchestration command dispatch duration." });
|
|
67069
|
+
const orchestrationCommandAckDuration = timer("t3_orchestration_command_ack_duration", { description: "Time from orchestration command dispatch to the first committed domain event emitted for that command." });
|
|
67070
|
+
const orchestrationEventsProcessedTotal = counter("t3_orchestration_events_processed_total", { description: "Total orchestration intent events processed by runtime reactors." });
|
|
67071
|
+
const providerSessionsTotal = counter("t3_provider_sessions_total", { description: "Total provider session lifecycle operations." });
|
|
67072
|
+
const providerTurnsTotal = counter("t3_provider_turns_total", { description: "Total provider turn lifecycle operations." });
|
|
67073
|
+
const providerTurnDuration = timer("t3_provider_turn_duration", { description: "Provider turn request duration." });
|
|
67074
|
+
const providerRuntimeEventsTotal = counter("t3_provider_runtime_events_total", { description: "Total canonical provider runtime events processed." });
|
|
67075
|
+
const gitCommandsTotal = counter("t3_git_commands_total", { description: "Total git commands executed by the server runtime." });
|
|
67076
|
+
const gitCommandDuration = timer("t3_git_command_duration", { description: "Git command execution duration." });
|
|
67077
|
+
const terminalSessionsTotal = counter("t3_terminal_sessions_total", { description: "Total terminal sessions started." });
|
|
67078
|
+
const browserSessionsReapedTotal = counter("browser_sessions_reaped_total", { description: "Browser sessions and Chromium processes reclaimed by the orphan reaper, by reason (orphan | ttl | stale_profile)." });
|
|
67079
|
+
const terminalRestartsTotal = counter("t3_terminal_restarts_total", { description: "Total terminal restart requests handled." });
|
|
67080
|
+
const runtimeReplicationOutboxDepth = gauge("t3_runtime_replication_outbox_depth", { description: "Current runtime replication outbox items by durable state." });
|
|
67081
|
+
const runtimeReplicationOutboxOldestEventAge = gauge("t3_runtime_replication_outbox_oldest_event_age_seconds", { description: "Age in seconds of the oldest undelivered runtime replication payload." });
|
|
67082
|
+
const runtimeReplicationPublicationAttemptsTotal = counter("t3_runtime_replication_publication_attempts_total", { description: "Total canonical runtime replication publication attempts." });
|
|
67083
|
+
const runtimeReplicationPublicationRetriesTotal = counter("t3_runtime_replication_publication_retries_total", { description: "Total runtime replication retries scheduled after recoverable failures." });
|
|
67084
|
+
const runtimeReplicationPublicationLatency = timer("t3_runtime_replication_publication_latency", { description: "Time from durable enqueue to canonical CMMD acknowledgement." });
|
|
67085
|
+
const cmmdCredentialRefreshTotal = 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)." });
|
|
67086
|
+
const cmmdRotationFenceAppliedTotal = counter("t3_cmmd_rotation_fence_applied_total", { description: "Sessions Forge refused to rotate CMMD credentials for, because the rotation fence applied." });
|
|
67087
|
+
const cmmdCredentialRowDeletedTotal = counter("t3_cmmd_credential_row_deleted_total", { description: "command_access_tokens rows actually deleted by clearUnrecoverableCmmdAuthForSession, by reason." });
|
|
67088
|
+
const metricAttributes = (attributes) => Object.entries(compactMetricAttributes(attributes));
|
|
67089
|
+
const increment = (metric, attributes, amount = 1) => update$3(withAttributes(metric, metricAttributes(attributes)), amount);
|
|
67090
|
+
const withMetricsImpl = (effect, options) => gen(function* () {
|
|
67091
|
+
const startedAt = Date.now();
|
|
67092
|
+
const exit$2 = yield* exit(effect);
|
|
67093
|
+
const duration = millis(Math.max(0, Date.now() - startedAt));
|
|
67094
|
+
const baseAttributes = typeof options.attributes === "function" ? options.attributes() : options.attributes ?? {};
|
|
67095
|
+
if (options.timer) yield* update$3(withAttributes(options.timer, metricAttributes(baseAttributes)), duration);
|
|
67096
|
+
if (options.counter) {
|
|
67097
|
+
const outcome = outcomeFromExit(exit$2);
|
|
67098
|
+
yield* update$3(withAttributes(options.counter, metricAttributes({
|
|
67099
|
+
...baseAttributes,
|
|
67100
|
+
outcome,
|
|
67101
|
+
...options.outcomeAttributes ? options.outcomeAttributes(outcome) : {}
|
|
67102
|
+
})), 1);
|
|
67103
|
+
}
|
|
67104
|
+
if (isSuccess(exit$2)) return exit$2.value;
|
|
67105
|
+
return yield* failCause$1(exit$2.cause);
|
|
67106
|
+
});
|
|
67107
|
+
const withMetrics = dual(2, withMetricsImpl);
|
|
67108
|
+
const providerMetricAttributes = (provider, extra) => compactMetricAttributes({
|
|
67109
|
+
provider,
|
|
67110
|
+
...extra
|
|
67111
|
+
});
|
|
67112
|
+
const providerTurnMetricAttributes = (input) => {
|
|
67113
|
+
const modelFamily = normalizeModelMetricLabel(input.model);
|
|
67114
|
+
return compactMetricAttributes({
|
|
67115
|
+
provider: input.provider,
|
|
67116
|
+
...modelFamily ? { modelFamily } : {},
|
|
67117
|
+
...input.extra
|
|
67118
|
+
});
|
|
67119
|
+
};
|
|
67120
|
+
//#endregion
|
|
66968
67121
|
//#region src/persistence/Errors.ts
|
|
66969
67122
|
var PersistenceSqlError = class extends TaggedErrorClass()("PersistenceSqlError", {
|
|
66970
67123
|
operation: String$1,
|
|
@@ -67655,8 +67808,14 @@ const scheduleExpiredForgeCloudOwnerCredentialMaintenance = sleep$4(minutes(5)).
|
|
|
67655
67808
|
*
|
|
67656
67809
|
* Clearing every active owner row unconditionally (the pre-CMMD-1871 behaviour)
|
|
67657
67810
|
* turned a single expired tab into a sign-out on every browser and device.
|
|
67811
|
+
*
|
|
67812
|
+
* Destroys a refresh token good for months, so every call is logged with a
|
|
67813
|
+
* `reason` naming the caller's own decision (never the credential itself) and
|
|
67814
|
+
* the actual row count deleted, at info level so it shows up without raising
|
|
67815
|
+
* the server's log level. `reason` is optional only so a caller that has not
|
|
67816
|
+
* been updated yet still compiles; every current caller passes one.
|
|
67658
67817
|
*/
|
|
67659
|
-
const clearUnrecoverableCmmdAuthForSession = (sessionId) => gen(function* () {
|
|
67818
|
+
const clearUnrecoverableCmmdAuthForSession = (sessionId, reason) => gen(function* () {
|
|
67660
67819
|
const sql = yield* SqlClient;
|
|
67661
67820
|
const rows = yield* sql`
|
|
67662
67821
|
DELETE FROM command_access_tokens
|
|
@@ -67681,6 +67840,14 @@ const clearUnrecoverableCmmdAuthForSession = (sessionId) => gen(function* () {
|
|
|
67681
67840
|
`.pipe(mapError(toPersistenceSqlError("CommandAccessTokenStore.clearUnrecoverableCmmdAuth:query")));
|
|
67682
67841
|
forgetCommandAccessTokenCachesForSession(sessionId);
|
|
67683
67842
|
for (const row of rows) forgetCommandAccessTokenCachesForSession(row.sessionId);
|
|
67843
|
+
if (rows.length > 0) {
|
|
67844
|
+
yield* logInfo("CMMD credential row deleted.", {
|
|
67845
|
+
sessionId,
|
|
67846
|
+
reason: reason ?? "unspecified",
|
|
67847
|
+
rowsDeleted: rows.length
|
|
67848
|
+
});
|
|
67849
|
+
yield* update$3(withAttributes(cmmdCredentialRowDeletedTotal, metricAttributes({ reason: reason ?? "unspecified" })), rows.length);
|
|
67850
|
+
}
|
|
67684
67851
|
});
|
|
67685
67852
|
/**
|
|
67686
67853
|
* Retire refresh credentials copied into historical desktop bearer sessions.
|
|
@@ -67736,6 +67903,23 @@ const isDesktopBearerAuthSession = (sessionId) => gen(function* () {
|
|
|
67736
67903
|
`.pipe(mapError(toPersistenceSqlError("CommandAccessTokenStore.isDesktopBearerSession:query")));
|
|
67737
67904
|
return rows[0]?.method === "bearer-session-token" && rows[0].clientDeviceType === "desktop";
|
|
67738
67905
|
});
|
|
67906
|
+
/**
|
|
67907
|
+
* The device type a session's bearer bootstrap declared, for log lines only.
|
|
67908
|
+
*
|
|
67909
|
+
* Mirrors `isDesktopBearerAuthSession`'s read but returns the raw column
|
|
67910
|
+
* instead of collapsing it into a rotation verdict — a log line wants to say
|
|
67911
|
+
* what made the request, not whether Forge may rotate for it. Never fails:
|
|
67912
|
+
* an unreadable or missing row logs as `"unknown"` rather than losing the
|
|
67913
|
+
* decision-point log line it is attached to.
|
|
67914
|
+
*/
|
|
67915
|
+
const readClientDeviceTypeForSession = (sessionId) => gen(function* () {
|
|
67916
|
+
return (yield* (yield* SqlClient)`
|
|
67917
|
+
SELECT client_device_type AS "clientDeviceType"
|
|
67918
|
+
FROM auth_sessions
|
|
67919
|
+
WHERE session_id = ${sessionId}
|
|
67920
|
+
LIMIT 1
|
|
67921
|
+
`.pipe(mapError(toPersistenceSqlError("CommandAccessTokenStore.readClientDeviceType:query"))))[0]?.clientDeviceType ?? "unknown";
|
|
67922
|
+
}).pipe(orElseSucceed(() => "unknown"));
|
|
67739
67923
|
//#endregion
|
|
67740
67924
|
//#region src/auth/http/oauthCookies.ts
|
|
67741
67925
|
const OAUTH_STATE_COOKIE = "forge_oauth_state";
|
|
@@ -67938,9 +68122,20 @@ const authLogoutRouteLayer = add$1("POST", "/api/auth/logout", gen(function* ()
|
|
|
67938
68122
|
onSuccess: (session) => session.sessionId
|
|
67939
68123
|
}));
|
|
67940
68124
|
if (sessionId) {
|
|
68125
|
+
yield* logInfo("Auth logout.", {
|
|
68126
|
+
sessionId,
|
|
68127
|
+
deviceType: yield* readClientDeviceTypeForSession(sessionId),
|
|
68128
|
+
outcome: "session-cleared",
|
|
68129
|
+
reason: cloudShell ? "forge-cloud-shell-session" : "local-session"
|
|
68130
|
+
});
|
|
67941
68131
|
if (!cloudShell) yield* sessions.revoke(sessionId).pipe(ignore({ log: false }));
|
|
67942
68132
|
yield* clearCommandAccessTokenForSession(sessionId).pipe(ignore({ log: false }));
|
|
67943
|
-
}
|
|
68133
|
+
} else yield* logInfo("Auth logout.", {
|
|
68134
|
+
sessionId: null,
|
|
68135
|
+
deviceType: "unknown",
|
|
68136
|
+
outcome: "no-op",
|
|
68137
|
+
reason: "no authenticated session presented"
|
|
68138
|
+
});
|
|
67944
68139
|
const secure = shouldUseSecureCookie(forgePublicUrlForRequest(request));
|
|
67945
68140
|
let response = yield* jsonUnsafe({ loggedOut: true }, { status: 200 }).pipe(setCookie(sessions.cookieName, "", {
|
|
67946
68141
|
expires: /* @__PURE__ */ new Date(0),
|
|
@@ -68539,16 +68734,32 @@ const authCommandBearerBootstrapRouteLayer = add$1("POST", "/api/auth/bootstrap/
|
|
|
68539
68734
|
if (limited) return limited;
|
|
68540
68735
|
const serverAuth = yield* ServerAuth;
|
|
68541
68736
|
const sessions = yield* SessionCredentialService;
|
|
68542
|
-
if (serverAuth.isForgeCloudShell?.() === true)
|
|
68543
|
-
|
|
68544
|
-
|
|
68545
|
-
|
|
68737
|
+
if (serverAuth.isForgeCloudShell?.() === true) {
|
|
68738
|
+
yield* logWarning$1("CMMD bearer bootstrap failed.", {
|
|
68739
|
+
deviceType: "unknown",
|
|
68740
|
+
outcome: "failed",
|
|
68741
|
+
reason: "forge-cloud-shell-rejects-non-cmmd-bootstrap"
|
|
68742
|
+
});
|
|
68743
|
+
return yield* new AuthError({
|
|
68744
|
+
message: "Forge Cloud accepts owner assertions from CMMD only.",
|
|
68745
|
+
status: 403
|
|
68746
|
+
});
|
|
68747
|
+
}
|
|
68546
68748
|
const payload = yield* schemaBodyJson(CommandBootstrapInput).pipe(mapError((cause) => new AuthError({
|
|
68547
68749
|
message: "Invalid CMMD OAuth bearer bootstrap payload.",
|
|
68548
68750
|
status: 400,
|
|
68549
68751
|
cause
|
|
68752
|
+
})), tapError(() => logWarning$1("CMMD bearer bootstrap failed.", {
|
|
68753
|
+
deviceType: "unknown",
|
|
68754
|
+
outcome: "failed",
|
|
68755
|
+
reason: "invalid-payload"
|
|
68756
|
+
})));
|
|
68757
|
+
const deviceType = payload.clientDeviceType ?? "desktop";
|
|
68758
|
+
const validated = yield* validateCommandBootstrapPayload(payload).pipe(tapError(() => logWarning$1("CMMD bearer bootstrap failed.", {
|
|
68759
|
+
deviceType,
|
|
68760
|
+
outcome: "failed",
|
|
68761
|
+
reason: "invalid-command-bootstrap-payload"
|
|
68550
68762
|
})));
|
|
68551
|
-
const validated = yield* validateCommandBootstrapPayload(payload);
|
|
68552
68763
|
const sessionSubject = resolveCommandOAuthSubject(validated.subjectUserId);
|
|
68553
68764
|
const issued = yield* sessions.issue({
|
|
68554
68765
|
method: "bearer-session-token",
|
|
@@ -68561,20 +68772,34 @@ const authCommandBearerBootstrapRouteLayer = add$1("POST", "/api/auth/bootstrap/
|
|
|
68561
68772
|
}).pipe(mapError((cause) => new AuthError({
|
|
68562
68773
|
message: "Failed to issue authenticated bearer session.",
|
|
68563
68774
|
cause
|
|
68775
|
+
})), tapError(() => logWarning$1("CMMD bearer bootstrap failed.", {
|
|
68776
|
+
deviceType,
|
|
68777
|
+
outcome: "failed",
|
|
68778
|
+
reason: "session-issuance-failed"
|
|
68564
68779
|
})));
|
|
68565
68780
|
const cmmdIdentity = yield* persistCommandBootstrapSessionArtifacts({
|
|
68566
68781
|
sessionId: issued.sessionId,
|
|
68567
68782
|
payload,
|
|
68568
68783
|
cmmdIdentity: validated.cmmdIdentity
|
|
68569
68784
|
});
|
|
68570
|
-
if (
|
|
68785
|
+
if (deviceType === "desktop" && !payload.refreshToken?.trim()) yield* clearDesktopBearerRefreshTokensForSubject({
|
|
68571
68786
|
subject: sessionSubject,
|
|
68572
68787
|
clientUserAgent: request.headers["user-agent"]
|
|
68573
68788
|
}).pipe(mapError((cause) => new AuthError({
|
|
68574
68789
|
message: "Failed to retire legacy desktop refresh credentials.",
|
|
68575
68790
|
status: 500,
|
|
68576
68791
|
cause
|
|
68792
|
+
})), tapError(() => logWarning$1("CMMD bearer bootstrap failed.", {
|
|
68793
|
+
sessionId: issued.sessionId,
|
|
68794
|
+
deviceType,
|
|
68795
|
+
outcome: "failed",
|
|
68796
|
+
reason: "legacy-desktop-refresh-retirement-failed"
|
|
68577
68797
|
})));
|
|
68798
|
+
yield* logInfo("CMMD bearer bootstrap succeeded.", {
|
|
68799
|
+
sessionId: issued.sessionId,
|
|
68800
|
+
deviceType,
|
|
68801
|
+
outcome: "succeeded"
|
|
68802
|
+
});
|
|
68578
68803
|
return jsonUnsafe({
|
|
68579
68804
|
authenticated: true,
|
|
68580
68805
|
role: issued.role,
|
|
@@ -69865,64 +70090,6 @@ function parseAttachmentIdFromRelativePath(relativePath) {
|
|
|
69865
70090
|
return id.length > 0 && !id.includes(".") ? id : null;
|
|
69866
70091
|
}
|
|
69867
70092
|
//#endregion
|
|
69868
|
-
//#region src/observability/Attributes.ts
|
|
69869
|
-
function isPlainObject$1(value) {
|
|
69870
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
69871
|
-
}
|
|
69872
|
-
function markSeen(value, seen) {
|
|
69873
|
-
if (seen.has(value)) return true;
|
|
69874
|
-
seen.add(value);
|
|
69875
|
-
return false;
|
|
69876
|
-
}
|
|
69877
|
-
function normalizeJsonValue(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
69878
|
-
if (value === null || value === void 0 || typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value ?? null;
|
|
69879
|
-
if (typeof value === "bigint") return value.toString();
|
|
69880
|
-
if (value instanceof Date) return Number.isNaN(value.getTime()) ? "Invalid Date" : value.toISOString();
|
|
69881
|
-
if (value instanceof Error) return {
|
|
69882
|
-
name: value.name,
|
|
69883
|
-
message: value.message,
|
|
69884
|
-
...value.stack ? { stack: value.stack } : {}
|
|
69885
|
-
};
|
|
69886
|
-
if (Array.isArray(value)) {
|
|
69887
|
-
if (markSeen(value, seen)) return "[Circular]";
|
|
69888
|
-
return value.map((entry) => normalizeJsonValue(entry, seen));
|
|
69889
|
-
}
|
|
69890
|
-
if (value instanceof Map) {
|
|
69891
|
-
if (markSeen(value, seen)) return "[Circular]";
|
|
69892
|
-
return Object.fromEntries(Array.from(value.entries(), ([key, entryValue]) => [String(key), normalizeJsonValue(entryValue, seen)]));
|
|
69893
|
-
}
|
|
69894
|
-
if (value instanceof Set) {
|
|
69895
|
-
if (markSeen(value, seen)) return "[Circular]";
|
|
69896
|
-
return Array.from(value.values(), (entry) => normalizeJsonValue(entry, seen));
|
|
69897
|
-
}
|
|
69898
|
-
if (!isPlainObject$1(value)) return String(value);
|
|
69899
|
-
if (markSeen(value, seen)) return "[Circular]";
|
|
69900
|
-
return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [key, normalizeJsonValue(entryValue, seen)]));
|
|
69901
|
-
}
|
|
69902
|
-
function compactTraceAttributes(attributes) {
|
|
69903
|
-
return Object.fromEntries(Object.entries(attributes).filter(([, value]) => value !== void 0).map(([key, value]) => [key, normalizeJsonValue(value)]));
|
|
69904
|
-
}
|
|
69905
|
-
function compactMetricAttributes(attributes) {
|
|
69906
|
-
return Object.fromEntries(Object.entries(attributes).flatMap(([key, value]) => {
|
|
69907
|
-
if (value === void 0 || value === null) return [];
|
|
69908
|
-
if (typeof value === "string") return [[key, value]];
|
|
69909
|
-
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return [[key, String(value)]];
|
|
69910
|
-
return [];
|
|
69911
|
-
}));
|
|
69912
|
-
}
|
|
69913
|
-
function outcomeFromExit(exit) {
|
|
69914
|
-
if (isSuccess(exit)) return "success";
|
|
69915
|
-
return hasInterruptsOnly(exit.cause) ? "interrupt" : "failure";
|
|
69916
|
-
}
|
|
69917
|
-
function normalizeModelMetricLabel(model) {
|
|
69918
|
-
const normalized = model?.trim().toLowerCase();
|
|
69919
|
-
if (!normalized) return;
|
|
69920
|
-
if (normalized.includes("gpt")) return "gpt";
|
|
69921
|
-
if (normalized.includes("claude")) return "claude";
|
|
69922
|
-
if (normalized.includes("gemini")) return "gemini";
|
|
69923
|
-
return "other";
|
|
69924
|
-
}
|
|
69925
|
-
//#endregion
|
|
69926
70093
|
//#region src/observability/TraceRecord.ts
|
|
69927
70094
|
function formatTraceExit(exit) {
|
|
69928
70095
|
if (isSuccess(exit)) return { _tag: "Success" };
|
|
@@ -70978,6 +71145,22 @@ async function readOAuthErrorCode(response) {
|
|
|
70978
71145
|
//#endregion
|
|
70979
71146
|
//#region src/command/cmmdRotationFence.ts
|
|
70980
71147
|
/**
|
|
71148
|
+
* Log and count a fence application, without changing the verdict it reports.
|
|
71149
|
+
*
|
|
71150
|
+
* The two facts named in the log line are exactly the ones the predicate
|
|
71151
|
+
* above the caller already computed — whether this session ever deposited a
|
|
71152
|
+
* refresh token of its own, and (only when that was false) whether the
|
|
71153
|
+
* session row reads as a desktop bearer session. Neither fact nor the log
|
|
71154
|
+
* line changes what the caller returns.
|
|
71155
|
+
*/
|
|
71156
|
+
function logRotationFenceApplied(input) {
|
|
71157
|
+
return logInfo("CMMD rotation fence applied: session may not rotate its own credential.", {
|
|
71158
|
+
sessionId: input.sessionId,
|
|
71159
|
+
hasStoredRefreshToken: input.hasStoredRefreshToken,
|
|
71160
|
+
isDesktopBearerSession: input.isDesktopBearerSession
|
|
71161
|
+
}).pipe(andThen(() => update$3(withAttributes(cmmdRotationFenceAppliedTotal, metricAttributes({})), 1)));
|
|
71162
|
+
}
|
|
71163
|
+
/**
|
|
70981
71164
|
* True when Forge must not rotate this session's CMMD credentials.
|
|
70982
71165
|
*
|
|
70983
71166
|
* Fails closed: if the session row cannot be read, assume the fence applies.
|
|
@@ -70985,8 +71168,22 @@ async function readOAuthErrorCode(response) {
|
|
|
70985
71168
|
* reconnect.
|
|
70986
71169
|
*/
|
|
70987
71170
|
function isRotationFencedSession(sessionId) {
|
|
70988
|
-
|
|
70989
|
-
|
|
71171
|
+
const hasStoredRefreshToken = readCommandRefreshTokenForSession(sessionId) !== null;
|
|
71172
|
+
if (hasStoredRefreshToken) return succeed(false);
|
|
71173
|
+
return isDesktopBearerAuthSession(sessionId).pipe(match$1({
|
|
71174
|
+
onFailure: () => ({
|
|
71175
|
+
fenced: true,
|
|
71176
|
+
isDesktopBearerSession: null
|
|
71177
|
+
}),
|
|
71178
|
+
onSuccess: (isDesktopBearerSession) => ({
|
|
71179
|
+
fenced: isDesktopBearerSession,
|
|
71180
|
+
isDesktopBearerSession
|
|
71181
|
+
})
|
|
71182
|
+
}), tap(({ fenced, isDesktopBearerSession }) => fenced ? logRotationFenceApplied({
|
|
71183
|
+
sessionId,
|
|
71184
|
+
hasStoredRefreshToken,
|
|
71185
|
+
isDesktopBearerSession
|
|
71186
|
+
}) : void_$1), map$3(({ fenced }) => fenced));
|
|
70990
71187
|
}
|
|
70991
71188
|
//#endregion
|
|
70992
71189
|
//#region src/cmmdConnection/cmmdCredentialRejection.ts
|
|
@@ -71023,7 +71220,7 @@ function forgetCmmdCredentialRejection(sessionId) {
|
|
|
71023
71220
|
*/
|
|
71024
71221
|
const clearCmmdAuthAfterCmmdRejection = (sessionId) => gen(function* () {
|
|
71025
71222
|
if (!hasCmmdCredentialRejection(sessionId)) return;
|
|
71026
|
-
yield* clearUnrecoverableCmmdAuthForSession(sessionId);
|
|
71223
|
+
yield* clearUnrecoverableCmmdAuthForSession(sessionId, "cmmd-rejected-credential");
|
|
71027
71224
|
forgetCmmdCredentialRejection(sessionId);
|
|
71028
71225
|
});
|
|
71029
71226
|
//#endregion
|
|
@@ -71034,6 +71231,23 @@ function usableCmmdUserJwt(token) {
|
|
|
71034
71231
|
function storeResolvedJwt(sessionId, accessToken, refreshToken) {
|
|
71035
71232
|
return setCommandJwtForSession(sessionId, accessToken, refreshToken ? { refreshToken } : {}).pipe(ignore({ log: true }));
|
|
71036
71233
|
}
|
|
71234
|
+
function logCmmdJwtRung(context, rung, outcome, reason) {
|
|
71235
|
+
return (outcome === "failed" ? logWarning$1 : logInfo)("CMMD JWT resolver rung.", {
|
|
71236
|
+
sessionId: context.sessionId,
|
|
71237
|
+
subject: context.subject,
|
|
71238
|
+
deviceType: context.deviceType,
|
|
71239
|
+
caller: context.caller,
|
|
71240
|
+
rung,
|
|
71241
|
+
outcome,
|
|
71242
|
+
reason
|
|
71243
|
+
});
|
|
71244
|
+
}
|
|
71245
|
+
function recordCmmdCredentialRefreshMetric(rung, outcome) {
|
|
71246
|
+
return update$3(withAttributes(cmmdCredentialRefreshTotal, metricAttributes({
|
|
71247
|
+
rung,
|
|
71248
|
+
outcome
|
|
71249
|
+
})), 1);
|
|
71250
|
+
}
|
|
71037
71251
|
const inFlightRefreshes = /* @__PURE__ */ new Map();
|
|
71038
71252
|
function refreshOnceForSession(sessionId, refreshToken) {
|
|
71039
71253
|
return promise(() => {
|
|
@@ -71055,15 +71269,21 @@ function refreshOnceForSession(sessionId, refreshToken) {
|
|
|
71055
71269
|
* sibling session's token silently invalidates that sibling's stored copy and
|
|
71056
71270
|
* signs it out.
|
|
71057
71271
|
*/
|
|
71058
|
-
function trySelfRefresh(sessionId,
|
|
71272
|
+
function trySelfRefresh(sessionId, context) {
|
|
71059
71273
|
const currentRefreshToken = readCommandRefreshTokenForSession(sessionId);
|
|
71060
|
-
if (!currentRefreshToken) return
|
|
71274
|
+
if (!currentRefreshToken) return logCmmdJwtRung(context, "self-refresh", "skipped", "no refresh token stored for this session").pipe(as$3(null));
|
|
71061
71275
|
return gen(function* () {
|
|
71276
|
+
yield* logCmmdJwtRung(context, "self-refresh", "attempted", "rotating this session's own stored refresh token");
|
|
71277
|
+
yield* recordCmmdCredentialRefreshMetric("self-refresh", "attempted");
|
|
71062
71278
|
const refreshed = yield* refreshOnceForSession(sessionId, currentRefreshToken);
|
|
71063
71279
|
const accessToken = usableCmmdUserJwt(refreshed?.accessToken);
|
|
71064
|
-
if (!accessToken)
|
|
71280
|
+
if (!accessToken) {
|
|
71281
|
+
yield* logCmmdJwtRung(context, "self-refresh", "failed", "CMMD refresh did not return a usable user access JWT");
|
|
71282
|
+
return null;
|
|
71283
|
+
}
|
|
71065
71284
|
yield* storeResolvedJwt(sessionId, accessToken, refreshed?.refreshToken);
|
|
71066
|
-
|
|
71285
|
+
yield* recordCmmdCredentialRefreshMetric("self-refresh", "succeeded");
|
|
71286
|
+
yield* logCmmdJwtRung(context, "self-refresh", "succeeded", "CMMD rotated the stored refresh token");
|
|
71067
71287
|
return accessToken;
|
|
71068
71288
|
});
|
|
71069
71289
|
}
|
|
@@ -71080,16 +71300,22 @@ function trySelfRefresh(sessionId, logPrefix) {
|
|
|
71080
71300
|
* CMMD owns its lifetime, and a transient network failure must not throw away
|
|
71081
71301
|
* the one credential that can still recover the session.
|
|
71082
71302
|
*/
|
|
71083
|
-
function tryGrantRedemption(sessionId,
|
|
71303
|
+
function tryGrantRedemption(sessionId, context) {
|
|
71084
71304
|
const grantToken = readCommandSessionGrantForSession(sessionId);
|
|
71085
|
-
if (!grantToken) return
|
|
71305
|
+
if (!grantToken) return logCmmdJwtRung(context, "grant-redemption", "skipped", "no session grant stored for this session").pipe(as$3(null));
|
|
71086
71306
|
return gen(function* () {
|
|
71307
|
+
yield* logCmmdJwtRung(context, "grant-redemption", "attempted", "redeeming this session's durable CMMD session grant");
|
|
71308
|
+
yield* recordCmmdCredentialRefreshMetric("grant-redemption", "attempted");
|
|
71087
71309
|
const redeemed = yield* promise(() => redeemCmmdSessionGrant(grantToken)).pipe(orElseSucceed(() => null));
|
|
71088
71310
|
const accessToken = usableCmmdUserJwt(redeemed?.accessToken);
|
|
71089
|
-
if (!accessToken)
|
|
71311
|
+
if (!accessToken) {
|
|
71312
|
+
yield* logCmmdJwtRung(context, "grant-redemption", "failed", "session grant redemption did not return a usable user access JWT");
|
|
71313
|
+
return null;
|
|
71314
|
+
}
|
|
71090
71315
|
if (redeemed?.mcpToken) yield* setCommandAccessTokenForSession(sessionId, redeemed.mcpToken).pipe(ignore({ log: true }));
|
|
71091
71316
|
yield* storeResolvedJwt(sessionId, accessToken, redeemed?.refreshToken);
|
|
71092
|
-
|
|
71317
|
+
yield* recordCmmdCredentialRefreshMetric("grant-redemption", "succeeded");
|
|
71318
|
+
yield* logCmmdJwtRung(context, "grant-redemption", "succeeded", "session grant redeemed");
|
|
71093
71319
|
return accessToken;
|
|
71094
71320
|
});
|
|
71095
71321
|
}
|
|
@@ -71100,23 +71326,28 @@ function tryGrantRedemption(sessionId, logPrefix) {
|
|
|
71100
71326
|
* fallback for when that rotation is already gone. Neither touches another
|
|
71101
71327
|
* session's credentials, so this ladder is safe to run from any path.
|
|
71102
71328
|
*/
|
|
71103
|
-
function tryOwnCredentialRecovery(sessionId,
|
|
71329
|
+
function tryOwnCredentialRecovery(sessionId, context) {
|
|
71104
71330
|
return gen(function* () {
|
|
71105
|
-
const refreshed = yield* trySelfRefresh(sessionId,
|
|
71331
|
+
const refreshed = yield* trySelfRefresh(sessionId, context);
|
|
71106
71332
|
if (refreshed) return refreshed;
|
|
71107
|
-
return yield* tryGrantRedemption(sessionId,
|
|
71333
|
+
return yield* tryGrantRedemption(sessionId, context);
|
|
71108
71334
|
});
|
|
71109
71335
|
}
|
|
71110
|
-
function tryPromoteBootstrap(sessionId, subject, forceRefresh) {
|
|
71111
|
-
if (subject !== "owner-bootstrap" && subject !== "desktop-bootstrap") return
|
|
71336
|
+
function tryPromoteBootstrap(sessionId, subject, forceRefresh, context) {
|
|
71337
|
+
if (subject !== "owner-bootstrap" && subject !== "desktop-bootstrap") return logCmmdJwtRung(context, "promote-bootstrap", "skipped", "subject is not a bootstrap subject").pipe(as$3(null));
|
|
71112
71338
|
return gen(function* () {
|
|
71339
|
+
yield* logCmmdJwtRung(context, "promote-bootstrap", "attempted", "promoting the most recent owner CMMD auth to this bootstrap session");
|
|
71113
71340
|
yield* promoteMostRecentOwnerCommandAuthToSession(sessionId).pipe(catch_((error) => logWarning$1("Failed to promote owner CMMD auth for REST JWT resolution.", {
|
|
71114
71341
|
cause: error,
|
|
71115
71342
|
sessionId,
|
|
71116
71343
|
subject
|
|
71117
71344
|
})));
|
|
71118
71345
|
const promotedJwt = usableCmmdUserJwt(readCommandJwtForSession(sessionId));
|
|
71119
|
-
if (!forceRefresh && promotedJwt && !isJwtExpiredForSession(sessionId))
|
|
71346
|
+
if (!forceRefresh && promotedJwt && !isJwtExpiredForSession(sessionId)) {
|
|
71347
|
+
yield* logCmmdJwtRung(context, "promote-bootstrap", "succeeded", "promoted owner JWT is usable");
|
|
71348
|
+
return promotedJwt;
|
|
71349
|
+
}
|
|
71350
|
+
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");
|
|
71120
71351
|
return null;
|
|
71121
71352
|
});
|
|
71122
71353
|
}
|
|
@@ -71129,17 +71360,50 @@ function tryPromoteBootstrap(sessionId, subject, forceRefresh) {
|
|
|
71129
71360
|
* must stay private to the session it was issued to (see `trySelfRefresh`).
|
|
71130
71361
|
* Copying it is what let one expired tab sign the user out everywhere.
|
|
71131
71362
|
*/
|
|
71132
|
-
function trySubjectFallback(sessionId, subject,
|
|
71363
|
+
function trySubjectFallback(sessionId, subject, context) {
|
|
71133
71364
|
return gen(function* () {
|
|
71134
|
-
|
|
71135
|
-
const fallbackJwt = usableCmmdUserJwt(
|
|
71136
|
-
if (!fallbackJwt)
|
|
71365
|
+
yield* logCmmdJwtRung(context, "subject-fallback", "attempted", "looking for a sibling session's still-valid JWT for this subject");
|
|
71366
|
+
const fallbackJwt = usableCmmdUserJwt((yield* readMostRecentJwtForSubject(subject).pipe(orElseSucceed(() => null)))?.jwt);
|
|
71367
|
+
if (!fallbackJwt) {
|
|
71368
|
+
yield* logCmmdJwtRung(context, "subject-fallback", "failed", "no sibling session holds a still-valid JWT for this subject");
|
|
71369
|
+
return null;
|
|
71370
|
+
}
|
|
71137
71371
|
yield* storeResolvedJwt(sessionId, fallbackJwt, null);
|
|
71138
|
-
|
|
71372
|
+
yield* logCmmdJwtRung(context, "subject-fallback", "succeeded", "borrowed a sibling session's still-valid JWT");
|
|
71139
71373
|
return fallbackJwt;
|
|
71140
71374
|
});
|
|
71141
71375
|
}
|
|
71142
71376
|
/**
|
|
71377
|
+
* The rungs below the rotation fence: forced own-credential recovery,
|
|
71378
|
+
* bootstrap promotion, subject fallback, then own-credential recovery again
|
|
71379
|
+
* as the last resort. Split out of `resolveCmmdJwtForSession` so each half of
|
|
71380
|
+
* the ladder stays independently readable (and independently testable) —
|
|
71381
|
+
* the fence check above is the one decision the split does not touch.
|
|
71382
|
+
*/
|
|
71383
|
+
function runRecoveryLadder(sessionId, normalizedSubject, forceRefresh, context) {
|
|
71384
|
+
return gen(function* () {
|
|
71385
|
+
if (forceRefresh) {
|
|
71386
|
+
const forced = yield* tryOwnCredentialRecovery(sessionId, context);
|
|
71387
|
+
if (forced) return forced;
|
|
71388
|
+
}
|
|
71389
|
+
if (normalizedSubject) {
|
|
71390
|
+
const promoted = yield* tryPromoteBootstrap(sessionId, normalizedSubject, forceRefresh, context);
|
|
71391
|
+
if (promoted) return promoted;
|
|
71392
|
+
}
|
|
71393
|
+
if (!normalizedSubject) {
|
|
71394
|
+
yield* logCmmdJwtRung(context, "cache", "failed", "no subject to resolve and forced recovery already exhausted");
|
|
71395
|
+
return null;
|
|
71396
|
+
}
|
|
71397
|
+
if (!forceRefresh) {
|
|
71398
|
+
const fallback = yield* trySubjectFallback(sessionId, normalizedSubject, context);
|
|
71399
|
+
if (fallback) return fallback;
|
|
71400
|
+
}
|
|
71401
|
+
const recovered = yield* tryOwnCredentialRecovery(sessionId, context);
|
|
71402
|
+
if (!recovered) yield* logCmmdJwtRung(context, "cache", "failed", "recovery ladder exhausted: no rung produced a usable JWT");
|
|
71403
|
+
return recovered;
|
|
71404
|
+
});
|
|
71405
|
+
}
|
|
71406
|
+
/**
|
|
71143
71407
|
* Resolve the CMMD REST JWT for a Forge session.
|
|
71144
71408
|
*
|
|
71145
71409
|
* Forge session auth is long-lived compared with the short-lived CMMD JWT.
|
|
@@ -71153,26 +71417,30 @@ function resolveCmmdJwtForSession({ sessionId, subject, logPrefix = "cmmd-auth",
|
|
|
71153
71417
|
const cached = usableCmmdUserJwt(readCommandJwtForSession(sessionId));
|
|
71154
71418
|
if (cached && !isJwtExpiredForSession(sessionId)) {
|
|
71155
71419
|
if (!forceRefresh) return succeed(cached);
|
|
71156
|
-
if (staleAccessToken && cached !== staleAccessToken) return succeed(cached)
|
|
71420
|
+
if (staleAccessToken && cached !== staleAccessToken) return succeed(cached).pipe(tap(() => gen(function* () {
|
|
71421
|
+
const deviceType = yield* readClientDeviceTypeForSession(sessionId);
|
|
71422
|
+
yield* logCmmdJwtRung({
|
|
71423
|
+
sessionId,
|
|
71424
|
+
subject: subject?.trim() || null,
|
|
71425
|
+
deviceType,
|
|
71426
|
+
caller: logPrefix
|
|
71427
|
+
}, "cache", "skipped", "forced refresh skipped: cache already holds a token another caller rotated");
|
|
71428
|
+
})));
|
|
71157
71429
|
}
|
|
71158
71430
|
const normalizedSubject = subject?.trim() || null;
|
|
71159
71431
|
if (!normalizedSubject && !forceRefresh) return succeed(null);
|
|
71160
71432
|
return gen(function* () {
|
|
71161
|
-
|
|
71162
|
-
|
|
71163
|
-
|
|
71164
|
-
|
|
71165
|
-
|
|
71166
|
-
|
|
71167
|
-
|
|
71168
|
-
|
|
71169
|
-
|
|
71170
|
-
if (!normalizedSubject) return null;
|
|
71171
|
-
if (!forceRefresh) {
|
|
71172
|
-
const fallback = yield* trySubjectFallback(sessionId, normalizedSubject, logPrefix);
|
|
71173
|
-
if (fallback) return fallback;
|
|
71433
|
+
const context = {
|
|
71434
|
+
sessionId,
|
|
71435
|
+
subject: normalizedSubject,
|
|
71436
|
+
deviceType: yield* readClientDeviceTypeForSession(sessionId),
|
|
71437
|
+
caller: logPrefix
|
|
71438
|
+
};
|
|
71439
|
+
if (yield* isRotationFencedSession(sessionId)) {
|
|
71440
|
+
yield* logCmmdJwtRung(context, "rotation-fence", "failed", "session is fenced from rotating CMMD credentials");
|
|
71441
|
+
return null;
|
|
71174
71442
|
}
|
|
71175
|
-
return yield*
|
|
71443
|
+
return yield* runRecoveryLadder(sessionId, normalizedSubject, forceRefresh, context);
|
|
71176
71444
|
});
|
|
71177
71445
|
}
|
|
71178
71446
|
//#endregion
|
|
@@ -71909,9 +72177,14 @@ function buildProxyErrorResponse(error) {
|
|
|
71909
72177
|
* warning rather than failing the request if the clear itself errors — a
|
|
71910
72178
|
* session that already has no usable credential must still get its 401,
|
|
71911
72179
|
* not a 500 from a failed best-effort cleanup.
|
|
72180
|
+
*
|
|
72181
|
+
* Reached only after CMMD itself rejected the request with both the original
|
|
72182
|
+
* and (if retried) the refreshed JWT, so — unlike `respondCmmdConnectionRequired`
|
|
72183
|
+
* below — this always deletes the row. `clearUnrecoverableCmmdAuthForSession`
|
|
72184
|
+
* logs the row count and the reason itself; this only names the reason.
|
|
71912
72185
|
*/
|
|
71913
72186
|
function clearCmmdAuthAfterFailure(sessionId) {
|
|
71914
|
-
return clearUnrecoverableCmmdAuthForSession(sessionId).pipe(catch_((error) => logWarning$1("Failed to clear unrecoverable CMMD auth after CMMD auth failure.", {
|
|
72187
|
+
return clearUnrecoverableCmmdAuthForSession(sessionId, "cmmd-proxy-auth-error").pipe(catch_((error) => logWarning$1("Failed to clear unrecoverable CMMD auth after CMMD auth failure.", {
|
|
71915
72188
|
cause: error,
|
|
71916
72189
|
sessionId
|
|
71917
72190
|
})));
|
|
@@ -71932,7 +72205,7 @@ function clearCmmdAuthAfterFailure(sessionId) {
|
|
|
71932
72205
|
* refused the credential, which `clearCmmdAuthAfterCmmdRejection` checks.
|
|
71933
72206
|
*/
|
|
71934
72207
|
function respondCmmdConnectionRequired(sessionId) {
|
|
71935
|
-
return
|
|
72208
|
+
return logInfo("CMMD proxy request answered CMMD_AUTH_REQUIRED.", { sessionId }).pipe(andThen(() => clearCmmdAuthAfterCmmdRejection(sessionId)), catch_((error) => logWarning$1("Failed to clear CMMD auth that CMMD itself rejected.", {
|
|
71936
72209
|
cause: error,
|
|
71937
72210
|
sessionId
|
|
71938
72211
|
})), as$3(buildProxyErrorResponse(new CmmdProxyAuthenticationRequiredError("CMMD connection required. Reconnect CMMD to continue."))));
|
|
@@ -94672,60 +94945,6 @@ const dispatchBootstrapTurnStart = (input, command) => gen(function* () {
|
|
|
94672
94945
|
*/
|
|
94673
94946
|
var OrchestrationEngineService = class extends Service$1()("forge/orchestration/Services/OrchestrationEngine/OrchestrationEngineService") {};
|
|
94674
94947
|
//#endregion
|
|
94675
|
-
//#region src/observability/Metrics.ts
|
|
94676
|
-
const rpcRequestsTotal = counter("t3_rpc_requests_total", { description: "Total RPC requests handled by the websocket RPC server." });
|
|
94677
|
-
const rpcRequestDuration = timer("t3_rpc_request_duration", { description: "RPC request handling duration." });
|
|
94678
|
-
const orchestrationCommandsTotal = counter("t3_orchestration_commands_total", { description: "Total orchestration commands dispatched." });
|
|
94679
|
-
const orchestrationCommandDuration = timer("t3_orchestration_command_duration", { description: "Orchestration command dispatch duration." });
|
|
94680
|
-
const orchestrationCommandAckDuration = timer("t3_orchestration_command_ack_duration", { description: "Time from orchestration command dispatch to the first committed domain event emitted for that command." });
|
|
94681
|
-
const orchestrationEventsProcessedTotal = counter("t3_orchestration_events_processed_total", { description: "Total orchestration intent events processed by runtime reactors." });
|
|
94682
|
-
const providerSessionsTotal = counter("t3_provider_sessions_total", { description: "Total provider session lifecycle operations." });
|
|
94683
|
-
const providerTurnsTotal = counter("t3_provider_turns_total", { description: "Total provider turn lifecycle operations." });
|
|
94684
|
-
const providerTurnDuration = timer("t3_provider_turn_duration", { description: "Provider turn request duration." });
|
|
94685
|
-
const providerRuntimeEventsTotal = counter("t3_provider_runtime_events_total", { description: "Total canonical provider runtime events processed." });
|
|
94686
|
-
const gitCommandsTotal = counter("t3_git_commands_total", { description: "Total git commands executed by the server runtime." });
|
|
94687
|
-
const gitCommandDuration = timer("t3_git_command_duration", { description: "Git command execution duration." });
|
|
94688
|
-
const terminalSessionsTotal = counter("t3_terminal_sessions_total", { description: "Total terminal sessions started." });
|
|
94689
|
-
const browserSessionsReapedTotal = counter("browser_sessions_reaped_total", { description: "Browser sessions and Chromium processes reclaimed by the orphan reaper, by reason (orphan | ttl | stale_profile)." });
|
|
94690
|
-
const terminalRestartsTotal = counter("t3_terminal_restarts_total", { description: "Total terminal restart requests handled." });
|
|
94691
|
-
const runtimeReplicationOutboxDepth = gauge("t3_runtime_replication_outbox_depth", { description: "Current runtime replication outbox items by durable state." });
|
|
94692
|
-
const runtimeReplicationOutboxOldestEventAge = gauge("t3_runtime_replication_outbox_oldest_event_age_seconds", { description: "Age in seconds of the oldest undelivered runtime replication payload." });
|
|
94693
|
-
const runtimeReplicationPublicationAttemptsTotal = counter("t3_runtime_replication_publication_attempts_total", { description: "Total canonical runtime replication publication attempts." });
|
|
94694
|
-
const runtimeReplicationPublicationRetriesTotal = counter("t3_runtime_replication_publication_retries_total", { description: "Total runtime replication retries scheduled after recoverable failures." });
|
|
94695
|
-
const runtimeReplicationPublicationLatency = timer("t3_runtime_replication_publication_latency", { description: "Time from durable enqueue to canonical CMMD acknowledgement." });
|
|
94696
|
-
const metricAttributes = (attributes) => Object.entries(compactMetricAttributes(attributes));
|
|
94697
|
-
const increment = (metric, attributes, amount = 1) => update$3(withAttributes(metric, metricAttributes(attributes)), amount);
|
|
94698
|
-
const withMetricsImpl = (effect, options) => gen(function* () {
|
|
94699
|
-
const startedAt = Date.now();
|
|
94700
|
-
const exit$2 = yield* exit(effect);
|
|
94701
|
-
const duration = millis(Math.max(0, Date.now() - startedAt));
|
|
94702
|
-
const baseAttributes = typeof options.attributes === "function" ? options.attributes() : options.attributes ?? {};
|
|
94703
|
-
if (options.timer) yield* update$3(withAttributes(options.timer, metricAttributes(baseAttributes)), duration);
|
|
94704
|
-
if (options.counter) {
|
|
94705
|
-
const outcome = outcomeFromExit(exit$2);
|
|
94706
|
-
yield* update$3(withAttributes(options.counter, metricAttributes({
|
|
94707
|
-
...baseAttributes,
|
|
94708
|
-
outcome,
|
|
94709
|
-
...options.outcomeAttributes ? options.outcomeAttributes(outcome) : {}
|
|
94710
|
-
})), 1);
|
|
94711
|
-
}
|
|
94712
|
-
if (isSuccess(exit$2)) return exit$2.value;
|
|
94713
|
-
return yield* failCause$1(exit$2.cause);
|
|
94714
|
-
});
|
|
94715
|
-
const withMetrics = dual(2, withMetricsImpl);
|
|
94716
|
-
const providerMetricAttributes = (provider, extra) => compactMetricAttributes({
|
|
94717
|
-
provider,
|
|
94718
|
-
...extra
|
|
94719
|
-
});
|
|
94720
|
-
const providerTurnMetricAttributes = (input) => {
|
|
94721
|
-
const modelFamily = normalizeModelMetricLabel(input.model);
|
|
94722
|
-
return compactMetricAttributes({
|
|
94723
|
-
provider: input.provider,
|
|
94724
|
-
...modelFamily ? { modelFamily } : {},
|
|
94725
|
-
...input.extra
|
|
94726
|
-
});
|
|
94727
|
-
};
|
|
94728
|
-
//#endregion
|
|
94729
94948
|
//#region src/observability/RpcInstrumentation.ts
|
|
94730
94949
|
const annotateRpcSpan = (method, traceAttributes) => annotateCurrentSpan({
|
|
94731
94950
|
"rpc.method": method,
|
|
@@ -96464,6 +96683,7 @@ const registerCloudRuntimeForReplicaHydration = gen(function* () {
|
|
|
96464
96683
|
credentialEpoch,
|
|
96465
96684
|
activeTurnCount: 0,
|
|
96466
96685
|
activeSessionCount: 0,
|
|
96686
|
+
acceptingTurns: false,
|
|
96467
96687
|
...forgeGitCommit !== void 0 ? { forgeGitCommit } : {},
|
|
96468
96688
|
forgeServerVersion: installedForgeServerVersion
|
|
96469
96689
|
}),
|
|
@@ -96500,6 +96720,128 @@ const hydrateCloudRuntimeReplica = gen(function* () {
|
|
|
96500
96720
|
return yield* hydrateRuntimeReplica({ expectedRuntimeId: grant.runtimeId }, dependencies);
|
|
96501
96721
|
});
|
|
96502
96722
|
//#endregion
|
|
96723
|
+
//#region src/runtimeDrain/RuntimeDrainState.ts
|
|
96724
|
+
/**
|
|
96725
|
+
* The positive facts this process must observe before it can claim a turn.
|
|
96726
|
+
*
|
|
96727
|
+
* Every entry is required, so adding one here is the whole change: the
|
|
96728
|
+
* derivation below reads this list rather than naming fields, and the record
|
|
96729
|
+
* it indexes cannot be built with a member missing.
|
|
96730
|
+
*/
|
|
96731
|
+
const RUNTIME_EXECUTION_PRECONDITIONS = ["orchestrationEngine", "providerLayer"];
|
|
96732
|
+
/**
|
|
96733
|
+
* The single definition of the accepting-turns fact.
|
|
96734
|
+
*
|
|
96735
|
+
* Exported so test stubs of this service derive it exactly as the live state
|
|
96736
|
+
* does. A stub that recomputed it by hand could disagree with production and
|
|
96737
|
+
* still pass.
|
|
96738
|
+
*/
|
|
96739
|
+
function deriveAcceptingTurns(input) {
|
|
96740
|
+
return RUNTIME_EXECUTION_PRECONDITIONS.every((precondition) => input.preconditions[precondition]) && !input.draining && !input.shutdownRequested;
|
|
96741
|
+
}
|
|
96742
|
+
const initialState = {
|
|
96743
|
+
activeTurns: /* @__PURE__ */ new Set(),
|
|
96744
|
+
activeTurnCount: 0,
|
|
96745
|
+
activeSessionCount: 0,
|
|
96746
|
+
draining: false,
|
|
96747
|
+
drainReason: null,
|
|
96748
|
+
drainTargetImageRef: null,
|
|
96749
|
+
drainTargetImageVersion: null,
|
|
96750
|
+
drainForceRequired: false,
|
|
96751
|
+
preconditions: {
|
|
96752
|
+
orchestrationEngine: false,
|
|
96753
|
+
providerLayer: false
|
|
96754
|
+
},
|
|
96755
|
+
shutdownRequested: false
|
|
96756
|
+
};
|
|
96757
|
+
function snapshotFromState(state) {
|
|
96758
|
+
return {
|
|
96759
|
+
activeTurnCount: state.activeTurnCount,
|
|
96760
|
+
activeSessionCount: state.activeSessionCount,
|
|
96761
|
+
draining: state.draining,
|
|
96762
|
+
drainReason: state.drainReason,
|
|
96763
|
+
drainTargetImageRef: state.drainTargetImageRef,
|
|
96764
|
+
drainTargetImageVersion: state.drainTargetImageVersion,
|
|
96765
|
+
drainForceRequired: state.drainForceRequired,
|
|
96766
|
+
acceptingTurns: deriveAcceptingTurns(state)
|
|
96767
|
+
};
|
|
96768
|
+
}
|
|
96769
|
+
function turnKey(input) {
|
|
96770
|
+
return `${input.threadId}:${input.turnId ?? "unknown"}`;
|
|
96771
|
+
}
|
|
96772
|
+
function normalizeCount(count) {
|
|
96773
|
+
return Number.isFinite(count) ? Math.max(0, Math.floor(count)) : 0;
|
|
96774
|
+
}
|
|
96775
|
+
const makeRuntimeDrainState = gen(function* () {
|
|
96776
|
+
const stateRef = yield* make$50(initialState);
|
|
96777
|
+
const update = (f) => updateAndGet(stateRef, f).pipe(map$3(snapshotFromState));
|
|
96778
|
+
return {
|
|
96779
|
+
beginTurn: (input) => update((state) => {
|
|
96780
|
+
const activeTurns = new Set(state.activeTurns);
|
|
96781
|
+
activeTurns.add(turnKey(input));
|
|
96782
|
+
return {
|
|
96783
|
+
...state,
|
|
96784
|
+
activeTurns,
|
|
96785
|
+
activeTurnCount: activeTurns.size
|
|
96786
|
+
};
|
|
96787
|
+
}),
|
|
96788
|
+
completeTurn: (input) => update((state) => {
|
|
96789
|
+
const activeTurns = new Set(state.activeTurns);
|
|
96790
|
+
activeTurns.delete(turnKey(input));
|
|
96791
|
+
return {
|
|
96792
|
+
...state,
|
|
96793
|
+
activeTurns,
|
|
96794
|
+
activeTurnCount: activeTurns.size
|
|
96795
|
+
};
|
|
96796
|
+
}),
|
|
96797
|
+
clearActiveTurns: update((state) => ({
|
|
96798
|
+
...state,
|
|
96799
|
+
activeTurns: /* @__PURE__ */ new Set(),
|
|
96800
|
+
activeTurnCount: 0
|
|
96801
|
+
})),
|
|
96802
|
+
setActiveSessionCount: (count) => update((state) => ({
|
|
96803
|
+
...state,
|
|
96804
|
+
activeSessionCount: normalizeCount(count)
|
|
96805
|
+
})),
|
|
96806
|
+
reconcileProviderSessions: (sessions) => update((state) => {
|
|
96807
|
+
const activeSessions = sessions.filter((session) => session.status !== "closed");
|
|
96808
|
+
const activeTurns = new Set(activeSessions.flatMap((session) => session.activeTurnId === void 0 ? [] : [turnKey({
|
|
96809
|
+
threadId: session.threadId,
|
|
96810
|
+
turnId: session.activeTurnId
|
|
96811
|
+
})]));
|
|
96812
|
+
return {
|
|
96813
|
+
...state,
|
|
96814
|
+
activeTurns,
|
|
96815
|
+
activeTurnCount: activeTurns.size,
|
|
96816
|
+
activeSessionCount: activeSessions.length
|
|
96817
|
+
};
|
|
96818
|
+
}),
|
|
96819
|
+
applyControlPlaneState: (input) => update((state) => ({
|
|
96820
|
+
...state,
|
|
96821
|
+
draining: input.draining ?? state.draining,
|
|
96822
|
+
drainReason: input.drainReason ?? state.drainReason,
|
|
96823
|
+
drainTargetImageRef: input.drainTargetImageRef ?? state.drainTargetImageRef,
|
|
96824
|
+
drainTargetImageVersion: input.drainTargetImageVersion ?? state.drainTargetImageVersion,
|
|
96825
|
+
drainForceRequired: input.drainForceRequired ?? state.drainForceRequired
|
|
96826
|
+
})),
|
|
96827
|
+
markExecutionPrecondition: (precondition) => update((state) => ({
|
|
96828
|
+
...state,
|
|
96829
|
+
preconditions: {
|
|
96830
|
+
...state.preconditions,
|
|
96831
|
+
[precondition]: true
|
|
96832
|
+
}
|
|
96833
|
+
})),
|
|
96834
|
+
requestShutdown: update((state) => ({
|
|
96835
|
+
...state,
|
|
96836
|
+
shutdownRequested: true
|
|
96837
|
+
})),
|
|
96838
|
+
snapshot: get$6(stateRef).pipe(map$3(snapshotFromState)),
|
|
96839
|
+
isDraining: get$6(stateRef).pipe(map$3((state) => state.draining))
|
|
96840
|
+
};
|
|
96841
|
+
});
|
|
96842
|
+
var RuntimeDrainState = class extends Service$1()("forge/runtimeDrain/RuntimeDrainState") {};
|
|
96843
|
+
const RuntimeDrainStateLive = effect(RuntimeDrainState, makeRuntimeDrainState);
|
|
96844
|
+
//#endregion
|
|
96503
96845
|
//#region src/persistence/Services/LocalAutomations.ts
|
|
96504
96846
|
const LocalAutomationStatus = Literals([
|
|
96505
96847
|
"active",
|
|
@@ -97906,6 +98248,28 @@ function serializeWorktreeLaunchActivity(activity) {
|
|
|
97906
98248
|
function decodeWorktreeLaunchActivityKind(kind) {
|
|
97907
98249
|
return isCmmdRuntimeWorktreeLaunchActivityKind(kind) ? kind : null;
|
|
97908
98250
|
}
|
|
98251
|
+
function planStepsOf(activity) {
|
|
98252
|
+
const decoded = decodeCmmdRuntimePlanActivity(encodeCmmdRuntimePlanActivityPayload(activity.kind, activity.payload));
|
|
98253
|
+
return decoded !== null && decoded.plan.length > 0 ? decoded.plan : null;
|
|
98254
|
+
}
|
|
98255
|
+
function serializePlanActivity(activity) {
|
|
98256
|
+
if (activity.kind !== "turn.plan.updated") return null;
|
|
98257
|
+
const plan = planStepsOf(activity);
|
|
98258
|
+
if (plan === null) return null;
|
|
98259
|
+
const completed = plan.filter((step) => step.status === "completed").length;
|
|
98260
|
+
return {
|
|
98261
|
+
sourceSequence: null,
|
|
98262
|
+
event: {
|
|
98263
|
+
eventId: `activity:${activity.id}`,
|
|
98264
|
+
occurredAt: canonicalRuntimeEventTimestamp(activity.createdAt),
|
|
98265
|
+
...eventTurn(activity.turnId),
|
|
98266
|
+
kind: "tool_call",
|
|
98267
|
+
summary: `Plan: ${completed} of ${plan.length} steps done`,
|
|
98268
|
+
status: "completed",
|
|
98269
|
+
payload: encodeCmmdRuntimePlanActivityPayload(activity.kind, activity.payload)
|
|
98270
|
+
}
|
|
98271
|
+
};
|
|
98272
|
+
}
|
|
97909
98273
|
function isReplicatedFailureActivity(activity) {
|
|
97910
98274
|
return activity.kind === "runtime.error" || activity.kind.startsWith("provider.") && activity.kind.endsWith(".failed");
|
|
97911
98275
|
}
|
|
@@ -98290,6 +98654,10 @@ function serializeRuntimeThreadEvents(thread) {
|
|
|
98290
98654
|
const event = serializeWorktreeLaunchActivity(activity);
|
|
98291
98655
|
return event === null ? [] : [event];
|
|
98292
98656
|
});
|
|
98657
|
+
const planActivities = thread.activities.flatMap((activity) => {
|
|
98658
|
+
const event = serializePlanActivity(activity);
|
|
98659
|
+
return event === null ? [] : [event];
|
|
98660
|
+
});
|
|
98293
98661
|
const recordedTurns = serializeRecordedTurns(thread);
|
|
98294
98662
|
const textGenerations = serializeTextGenerations(thread);
|
|
98295
98663
|
return [
|
|
@@ -98297,6 +98665,7 @@ function serializeRuntimeThreadEvents(thread) {
|
|
|
98297
98665
|
...replicaActivities,
|
|
98298
98666
|
...toolActivities,
|
|
98299
98667
|
...worktreeLaunchActivities,
|
|
98668
|
+
...planActivities,
|
|
98300
98669
|
...failureActivities,
|
|
98301
98670
|
...recordedTurns,
|
|
98302
98671
|
...textGenerations
|
|
@@ -100106,6 +100475,30 @@ const makeCommandGate = gen(function* () {
|
|
|
100106
100475
|
})
|
|
100107
100476
|
};
|
|
100108
100477
|
});
|
|
100478
|
+
/**
|
|
100479
|
+
* Settle the command gate for a finished startup, and report whether commands
|
|
100480
|
+
* are now accepted.
|
|
100481
|
+
*
|
|
100482
|
+
* The success branch is the moment the orchestration engine can take work, so
|
|
100483
|
+
* it is where the engine half of the accepting-turns fact is marked. A failed
|
|
100484
|
+
* startup accepts nothing and must leave the fact untouched: absent must not
|
|
100485
|
+
* read as healthy.
|
|
100486
|
+
*/
|
|
100487
|
+
const settleCommandReadiness = (input) => gen(function* () {
|
|
100488
|
+
if (isFailure(input.startupExit)) {
|
|
100489
|
+
const error = new ServerRuntimeStartupError({
|
|
100490
|
+
message: startupFailureMessage(input.startupExit.cause),
|
|
100491
|
+
cause: input.startupExit.cause
|
|
100492
|
+
});
|
|
100493
|
+
yield* logError("server runtime startup failed", { cause: describeStartupFailure(input.startupExit.cause) });
|
|
100494
|
+
yield* input.commandGate.failCommandReady(error);
|
|
100495
|
+
return false;
|
|
100496
|
+
}
|
|
100497
|
+
yield* logDebug("Accepting commands");
|
|
100498
|
+
yield* input.commandGate.signalCommandReady;
|
|
100499
|
+
yield* input.drainState.markExecutionPrecondition("orchestrationEngine");
|
|
100500
|
+
return true;
|
|
100501
|
+
});
|
|
100109
100502
|
const launchStartupHeartbeat = gen(function* () {
|
|
100110
100503
|
const analytics = yield* AnalyticsService;
|
|
100111
100504
|
const { threadCount, projectCount } = yield* (yield* ProjectionSnapshotQuery).getCounts().pipe(catch_((cause) => logWarning$1("failed to gather startup projection counts for telemetry", { cause }).pipe(as$3({
|
|
@@ -100286,6 +100679,7 @@ const ServerRuntimeStartupLive = effect(ServerRuntimeStartup, gen(function* () {
|
|
|
100286
100679
|
const serverSettings = yield* ServerSettingsService;
|
|
100287
100680
|
const serverEnvironment = yield* ServerEnvironment;
|
|
100288
100681
|
const usage = yield* UsageService;
|
|
100682
|
+
const drainState = yield* RuntimeDrainState;
|
|
100289
100683
|
const commandGate = yield* makeCommandGate;
|
|
100290
100684
|
const httpListening = yield* make$39();
|
|
100291
100685
|
const reactorScope = yield* make$37("sequential");
|
|
@@ -100368,18 +100762,11 @@ const ServerRuntimeStartupLive = effect(ServerRuntimeStartup, gen(function* () {
|
|
|
100368
100762
|
root: true
|
|
100369
100763
|
}));
|
|
100370
100764
|
yield* forkScoped(gen(function* () {
|
|
100371
|
-
|
|
100372
|
-
|
|
100373
|
-
|
|
100374
|
-
|
|
100375
|
-
|
|
100376
|
-
});
|
|
100377
|
-
yield* logError("server runtime startup failed", { cause: describeStartupFailure(startupExit.cause) });
|
|
100378
|
-
yield* commandGate.failCommandReady(error);
|
|
100379
|
-
return;
|
|
100380
|
-
}
|
|
100381
|
-
yield* logDebug("Accepting commands");
|
|
100382
|
-
yield* commandGate.signalCommandReady;
|
|
100765
|
+
if (!(yield* settleCommandReadiness({
|
|
100766
|
+
startupExit: yield* exit(startup),
|
|
100767
|
+
commandGate,
|
|
100768
|
+
drainState
|
|
100769
|
+
}))) return;
|
|
100383
100770
|
yield* logDebug("startup phase: starting the usage scan warm-up");
|
|
100384
100771
|
yield* forkScoped(runUsageScanWarmup({ readSummary: usage.readSummary }));
|
|
100385
100772
|
yield* logDebug("startup phase: waiting for http listener");
|
|
@@ -152962,7 +153349,9 @@ var CodexAppServerManager = class extends EventEmitter$1 {
|
|
|
152962
153349
|
scheduleProviderEgressTokenRefresh(context, delayMs) {
|
|
152963
153350
|
if (context.tokenRefreshTimer) clearTimeout(context.tokenRefreshTimer);
|
|
152964
153351
|
const tokenRefreshTimer = setTimeout(() => {
|
|
152965
|
-
this.handleProviderEgressTokenRefresh(context.session.threadId).catch(() =>
|
|
153352
|
+
this.handleProviderEgressTokenRefresh(context.session.threadId).catch((error) => {
|
|
153353
|
+
this.emitErrorEvent(context, "session/tokenRefreshFailed", error instanceof Error ? error.message : String(error));
|
|
153354
|
+
});
|
|
152966
153355
|
}, delayMs);
|
|
152967
153356
|
tokenRefreshTimer.unref?.();
|
|
152968
153357
|
context.tokenRefreshTimer = tokenRefreshTimer;
|
|
@@ -290225,93 +290614,6 @@ async function maybeRefreshRuntimeProvisioningGrant(input) {
|
|
|
290225
290614
|
return "refreshed";
|
|
290226
290615
|
}
|
|
290227
290616
|
//#endregion
|
|
290228
|
-
//#region src/runtimeDrain/RuntimeDrainState.ts
|
|
290229
|
-
const initialState = {
|
|
290230
|
-
activeTurns: /* @__PURE__ */ new Set(),
|
|
290231
|
-
activeTurnCount: 0,
|
|
290232
|
-
activeSessionCount: 0,
|
|
290233
|
-
draining: false,
|
|
290234
|
-
drainReason: null,
|
|
290235
|
-
drainTargetImageRef: null,
|
|
290236
|
-
drainTargetImageVersion: null,
|
|
290237
|
-
drainForceRequired: false
|
|
290238
|
-
};
|
|
290239
|
-
function snapshotFromState(state) {
|
|
290240
|
-
return {
|
|
290241
|
-
activeTurnCount: state.activeTurnCount,
|
|
290242
|
-
activeSessionCount: state.activeSessionCount,
|
|
290243
|
-
draining: state.draining,
|
|
290244
|
-
drainReason: state.drainReason,
|
|
290245
|
-
drainTargetImageRef: state.drainTargetImageRef,
|
|
290246
|
-
drainTargetImageVersion: state.drainTargetImageVersion,
|
|
290247
|
-
drainForceRequired: state.drainForceRequired
|
|
290248
|
-
};
|
|
290249
|
-
}
|
|
290250
|
-
function turnKey(input) {
|
|
290251
|
-
return `${input.threadId}:${input.turnId ?? "unknown"}`;
|
|
290252
|
-
}
|
|
290253
|
-
function normalizeCount(count) {
|
|
290254
|
-
return Number.isFinite(count) ? Math.max(0, Math.floor(count)) : 0;
|
|
290255
|
-
}
|
|
290256
|
-
const makeRuntimeDrainState = gen(function* () {
|
|
290257
|
-
const stateRef = yield* make$50(initialState);
|
|
290258
|
-
const update = (f) => updateAndGet(stateRef, f).pipe(map$3(snapshotFromState));
|
|
290259
|
-
return {
|
|
290260
|
-
beginTurn: (input) => update((state) => {
|
|
290261
|
-
const activeTurns = new Set(state.activeTurns);
|
|
290262
|
-
activeTurns.add(turnKey(input));
|
|
290263
|
-
return {
|
|
290264
|
-
...state,
|
|
290265
|
-
activeTurns,
|
|
290266
|
-
activeTurnCount: activeTurns.size
|
|
290267
|
-
};
|
|
290268
|
-
}),
|
|
290269
|
-
completeTurn: (input) => update((state) => {
|
|
290270
|
-
const activeTurns = new Set(state.activeTurns);
|
|
290271
|
-
activeTurns.delete(turnKey(input));
|
|
290272
|
-
return {
|
|
290273
|
-
...state,
|
|
290274
|
-
activeTurns,
|
|
290275
|
-
activeTurnCount: activeTurns.size
|
|
290276
|
-
};
|
|
290277
|
-
}),
|
|
290278
|
-
clearActiveTurns: update((state) => ({
|
|
290279
|
-
...state,
|
|
290280
|
-
activeTurns: /* @__PURE__ */ new Set(),
|
|
290281
|
-
activeTurnCount: 0
|
|
290282
|
-
})),
|
|
290283
|
-
setActiveSessionCount: (count) => update((state) => ({
|
|
290284
|
-
...state,
|
|
290285
|
-
activeSessionCount: normalizeCount(count)
|
|
290286
|
-
})),
|
|
290287
|
-
reconcileProviderSessions: (sessions) => update((state) => {
|
|
290288
|
-
const activeSessions = sessions.filter((session) => session.status !== "closed");
|
|
290289
|
-
const activeTurns = new Set(activeSessions.flatMap((session) => session.activeTurnId === void 0 ? [] : [turnKey({
|
|
290290
|
-
threadId: session.threadId,
|
|
290291
|
-
turnId: session.activeTurnId
|
|
290292
|
-
})]));
|
|
290293
|
-
return {
|
|
290294
|
-
...state,
|
|
290295
|
-
activeTurns,
|
|
290296
|
-
activeTurnCount: activeTurns.size,
|
|
290297
|
-
activeSessionCount: activeSessions.length
|
|
290298
|
-
};
|
|
290299
|
-
}),
|
|
290300
|
-
applyControlPlaneState: (input) => update((state) => ({
|
|
290301
|
-
...state,
|
|
290302
|
-
draining: input.draining ?? state.draining,
|
|
290303
|
-
drainReason: input.drainReason ?? state.drainReason,
|
|
290304
|
-
drainTargetImageRef: input.drainTargetImageRef ?? state.drainTargetImageRef,
|
|
290305
|
-
drainTargetImageVersion: input.drainTargetImageVersion ?? state.drainTargetImageVersion,
|
|
290306
|
-
drainForceRequired: input.drainForceRequired ?? state.drainForceRequired
|
|
290307
|
-
})),
|
|
290308
|
-
snapshot: get$6(stateRef).pipe(map$3(snapshotFromState)),
|
|
290309
|
-
isDraining: get$6(stateRef).pipe(map$3((state) => state.draining))
|
|
290310
|
-
};
|
|
290311
|
-
});
|
|
290312
|
-
var RuntimeDrainState = class extends Service$1()("forge/runtimeDrain/RuntimeDrainState") {};
|
|
290313
|
-
const RuntimeDrainStateLive = effect(RuntimeDrainState, makeRuntimeDrainState);
|
|
290314
|
-
//#endregion
|
|
290315
290617
|
//#region src/runtimeDrain/RuntimeDrainReporter.ts
|
|
290316
290618
|
const HEARTBEAT_INTERVAL = seconds(30);
|
|
290317
290619
|
const HEARTBEAT_TICK_TIMEOUT = seconds(60);
|
|
@@ -290369,6 +290671,7 @@ const postRuntimeHeartbeat = (config, snapshot) => promise(async () => {
|
|
|
290369
290671
|
...config,
|
|
290370
290672
|
activeTurnCount: snapshot.activeTurnCount,
|
|
290371
290673
|
activeSessionCount: snapshot.activeSessionCount,
|
|
290674
|
+
acceptingTurns: snapshot.acceptingTurns,
|
|
290372
290675
|
...forgeGitCommit !== void 0 ? { forgeGitCommit } : {},
|
|
290373
290676
|
forgeServerVersion: installedForgeServerVersion
|
|
290374
290677
|
});
|
|
@@ -290477,6 +290780,7 @@ const makeRuntimeDrainReporter = gen(function* () {
|
|
|
290477
290780
|
work: reportOnce,
|
|
290478
290781
|
interval: HEARTBEAT_INTERVAL
|
|
290479
290782
|
}));
|
|
290783
|
+
yield* addFinalizer$2(() => drainState.requestShutdown);
|
|
290480
290784
|
})
|
|
290481
290785
|
};
|
|
290482
290786
|
});
|
|
@@ -290687,6 +290991,7 @@ function decodeCodexRateLimitWindows(raw) {
|
|
|
290687
290991
|
function publicProviderRuntimeError(message) {
|
|
290688
290992
|
return sanitizePublicProviderText(message, { fallback: DEFAULT_PUBLIC_PROVIDER_ERROR });
|
|
290689
290993
|
}
|
|
290994
|
+
const processDomainEvent = (_event) => void_$1;
|
|
290690
290995
|
function isAssistantTextDeltaEvent(event) {
|
|
290691
290996
|
return event.type === "content.delta" && event.payload.streamKind === "assistant_text";
|
|
290692
290997
|
}
|
|
@@ -291480,7 +291785,6 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, fn$
|
|
|
291480
291785
|
yield* runtimeDrainReporter.reportOnce;
|
|
291481
291786
|
}
|
|
291482
291787
|
});
|
|
291483
|
-
const processDomainEvent = (_event) => void_$1;
|
|
291484
291788
|
const processInput = (input) => input.source === "runtime" ? processRuntimeEvent(input.event) : processDomainEvent(input.event);
|
|
291485
291789
|
const processInputSafely = (input) => processInput(input).pipe(catchCause((cause) => {
|
|
291486
291790
|
if (hasInterruptsOnly(cause)) return failCause$1(cause);
|
|
@@ -291531,6 +291835,7 @@ const ProviderRuntimeIngestionLive = effect(ProviderRuntimeIngestionService, fn$
|
|
|
291531
291835
|
return {
|
|
291532
291836
|
start: fn$1("start")(function* () {
|
|
291533
291837
|
yield* reconcileOrphanedRunningSessions();
|
|
291838
|
+
yield* runtimeDrainState.markExecutionPrecondition("providerLayer");
|
|
291534
291839
|
yield* runtimeDrainReporter.start;
|
|
291535
291840
|
yield* forkScoped(runForEach(providerService.streamEvents, (event) => enqueueInput({
|
|
291536
291841
|
source: "runtime",
|
|
@@ -291769,6 +292074,8 @@ const HANDLED_TURN_START_KEY_MAX = 1e4;
|
|
|
291769
292074
|
const HANDLED_TURN_START_KEY_TTL = minutes(30);
|
|
291770
292075
|
const DEFAULT_RUNTIME_MODE = "full-access";
|
|
291771
292076
|
const DEFAULT_THREAD_TITLE = "New thread";
|
|
292077
|
+
const isProviderValidationError = is$1(ProviderValidationError);
|
|
292078
|
+
const isProviderKind = is$1(ProviderKind);
|
|
291772
292079
|
function isPathInsideRoot(candidatePath, rootPath) {
|
|
291773
292080
|
let resolvedCandidate;
|
|
291774
292081
|
let resolvedRoot;
|
|
@@ -291805,6 +292112,38 @@ function resolveExistingWorkspaceCwd(input) {
|
|
|
291805
292112
|
function publicProviderFailureDetail(detail) {
|
|
291806
292113
|
return sanitizePublicProviderText(detail, { fallback: "CMMD is temporarily unavailable. Check the workspace AI connection and retry." }) ?? "CMMD is temporarily unavailable. Check the workspace AI connection and retry.";
|
|
291807
292114
|
}
|
|
292115
|
+
/**
|
|
292116
|
+
* The failure detail a transcript shows, judged on the text and not the envelope.
|
|
292117
|
+
*
|
|
292118
|
+
* Forge writes its own validation copy, so `ProviderValidationError.issue` is
|
|
292119
|
+
* not raw provider output. Every envelope around it hides it, each in its own
|
|
292120
|
+
* way: `Cause.pretty` prints the error tag, and the sanitizer counts
|
|
292121
|
+
* "ProviderValidationError" as a provider leak, so a validation failure
|
|
292122
|
+
* rendered through `Cause.pretty` tripped the guard on its own wrapper. The
|
|
292123
|
+
* `message` getter spells the same tag out in prose. On 2026-09-16 the first of
|
|
292124
|
+
* those replaced "Reconnect the account and try again" with "CMMD is
|
|
292125
|
+
* temporarily unavailable" (#3337).
|
|
292126
|
+
*
|
|
292127
|
+
* The issue still goes through the sanitizer. `ProviderService` builds some
|
|
292128
|
+
* issues from a schema formatter, which echoes the decoded payload, so this is a
|
|
292129
|
+
* narrower input to the same guard rather than a way around it.
|
|
292130
|
+
*/
|
|
292131
|
+
function publicProviderFailureDetailFromError(error) {
|
|
292132
|
+
if (isProviderValidationError(error)) return publicProviderFailureDetail(error.issue);
|
|
292133
|
+
return publicProviderFailureDetail(error instanceof Error ? error.message : String(error));
|
|
292134
|
+
}
|
|
292135
|
+
/**
|
|
292136
|
+
* The same judgement for a failure that arrives as a cause.
|
|
292137
|
+
*
|
|
292138
|
+
* Only the validation branch delegates. A cause carries what a bare error does
|
|
292139
|
+
* not: defects, interrupts, and more than one failure, all of which
|
|
292140
|
+
* `Cause.pretty` renders and `.message` would drop. Squashing every failure into
|
|
292141
|
+
* its message to reach one shared line would lose that.
|
|
292142
|
+
*/
|
|
292143
|
+
function publicProviderFailureDetailFromCause(cause) {
|
|
292144
|
+
const error = squash(cause);
|
|
292145
|
+
return isProviderValidationError(error) ? publicProviderFailureDetailFromError(error) : publicProviderFailureDetail(pretty(cause));
|
|
292146
|
+
}
|
|
291808
292147
|
function canReplaceThreadTitle(currentTitle, titleSeed) {
|
|
291809
292148
|
const trimmedCurrentTitle = currentTitle.trim();
|
|
291810
292149
|
if (trimmedCurrentTitle === DEFAULT_THREAD_TITLE) return true;
|
|
@@ -291960,7 +292299,7 @@ const ProviderCommandReactorLive = effect(ProviderCommandReactor, gen(function*
|
|
|
291960
292299
|
const thread = readModel.threads.find((entry) => entry.id === threadId);
|
|
291961
292300
|
if (!thread) return yield* die$1(/* @__PURE__ */ new Error(`Thread '${threadId}' was not found in read model.`));
|
|
291962
292301
|
const desiredRuntimeMode = thread.runtimeMode;
|
|
291963
|
-
const currentProvider = thread.session?.status !== "stopped" &&
|
|
292302
|
+
const currentProvider = thread.session?.status !== "stopped" && isProviderKind(thread.session?.providerName) ? thread.session.providerName : void 0;
|
|
291964
292303
|
const rawRequestedModelSelection = options?.modelSelection;
|
|
291965
292304
|
const threadProvider = currentProvider ?? thread.modelSelection.provider;
|
|
291966
292305
|
const isStaleRequestedProvider = rawRequestedModelSelection !== void 0 && rawRequestedModelSelection.provider !== threadProvider;
|
|
@@ -292287,7 +292626,7 @@ const ProviderCommandReactorLive = effect(ProviderCommandReactor, gen(function*
|
|
|
292287
292626
|
summary: "Provider turn start failed",
|
|
292288
292627
|
detail: `User message '${event.payload.messageId}' was not found for turn start request.`,
|
|
292289
292628
|
turnId: null,
|
|
292290
|
-
createdAt:
|
|
292629
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
292291
292630
|
});
|
|
292292
292631
|
return;
|
|
292293
292632
|
}
|
|
@@ -292338,9 +292677,9 @@ const ProviderCommandReactorLive = effect(ProviderCommandReactor, gen(function*
|
|
|
292338
292677
|
threadId: event.payload.threadId,
|
|
292339
292678
|
kind: "provider.turn.start.failed",
|
|
292340
292679
|
summary: "Provider turn start failed",
|
|
292341
|
-
detail:
|
|
292680
|
+
detail: publicProviderFailureDetailFromCause(cause),
|
|
292342
292681
|
turnId: null,
|
|
292343
|
-
createdAt:
|
|
292682
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
292344
292683
|
})));
|
|
292345
292684
|
});
|
|
292346
292685
|
const processTurnInterruptRequested = fn$1("processTurnInterruptRequested")(function* (event) {
|
|
@@ -292354,7 +292693,14 @@ const ProviderCommandReactorLive = effect(ProviderCommandReactor, gen(function*
|
|
|
292354
292693
|
turnId: event.payload.turnId ?? null,
|
|
292355
292694
|
createdAt: event.payload.createdAt
|
|
292356
292695
|
});
|
|
292357
|
-
yield* providerService.interruptTurn({ threadId: event.payload.threadId })
|
|
292696
|
+
yield* providerService.interruptTurn({ threadId: event.payload.threadId }).pipe(catchCause((cause) => appendProviderFailureActivity({
|
|
292697
|
+
threadId: event.payload.threadId,
|
|
292698
|
+
kind: "provider.turn.interrupt.failed",
|
|
292699
|
+
summary: "Provider turn interrupt failed",
|
|
292700
|
+
detail: publicProviderFailureDetail(pretty(cause)),
|
|
292701
|
+
turnId: event.payload.turnId ?? null,
|
|
292702
|
+
createdAt: event.payload.createdAt
|
|
292703
|
+
})));
|
|
292358
292704
|
});
|
|
292359
292705
|
const processApprovalResponseRequested = fn$1("processApprovalResponseRequested")(function* (event) {
|
|
292360
292706
|
const staged = peekThreadContinuationSession(event.commandId);
|
|
@@ -292391,7 +292737,7 @@ const ProviderCommandReactorLive = effect(ProviderCommandReactor, gen(function*
|
|
|
292391
292737
|
threadId: event.payload.threadId,
|
|
292392
292738
|
kind: "provider.approval.respond.failed",
|
|
292393
292739
|
summary: "Provider approval response failed",
|
|
292394
|
-
detail: isUnknownPendingApprovalRequestError(cause) ? stalePendingRequestDetail("approval", event.payload.requestId) :
|
|
292740
|
+
detail: isUnknownPendingApprovalRequestError(cause) ? stalePendingRequestDetail("approval", event.payload.requestId) : publicProviderFailureDetailFromCause(cause),
|
|
292395
292741
|
turnId: null,
|
|
292396
292742
|
createdAt: event.payload.createdAt,
|
|
292397
292743
|
requestId: event.payload.requestId
|
|
@@ -292434,7 +292780,7 @@ const ProviderCommandReactorLive = effect(ProviderCommandReactor, gen(function*
|
|
|
292434
292780
|
threadId: event.payload.threadId,
|
|
292435
292781
|
kind: "provider.user-input.respond.failed",
|
|
292436
292782
|
summary: "Provider user input response failed",
|
|
292437
|
-
detail: isUnknownPendingUserInputRequestError(cause) ? stalePendingRequestDetail("user-input", event.payload.requestId) :
|
|
292783
|
+
detail: isUnknownPendingUserInputRequestError(cause) ? stalePendingRequestDetail("user-input", event.payload.requestId) : publicProviderFailureDetailFromCause(cause),
|
|
292438
292784
|
turnId: null,
|
|
292439
292785
|
createdAt: event.payload.createdAt,
|
|
292440
292786
|
requestId: event.payload.requestId
|
|
@@ -292447,8 +292793,7 @@ const ProviderCommandReactorLive = effect(ProviderCommandReactor, gen(function*
|
|
|
292447
292793
|
yield* providerService.stopSession({
|
|
292448
292794
|
threadId: thread.id,
|
|
292449
292795
|
...event.payload.preserveResumeState === true ? { preserveResumeState: true } : {}
|
|
292450
|
-
})
|
|
292451
|
-
yield* setThreadSession({
|
|
292796
|
+
}).pipe(tap(() => setThreadSession({
|
|
292452
292797
|
threadId: thread.id,
|
|
292453
292798
|
session: {
|
|
292454
292799
|
threadId: thread.id,
|
|
@@ -292460,7 +292805,14 @@ const ProviderCommandReactorLive = effect(ProviderCommandReactor, gen(function*
|
|
|
292460
292805
|
updatedAt: now
|
|
292461
292806
|
},
|
|
292462
292807
|
createdAt: now
|
|
292463
|
-
})
|
|
292808
|
+
})), catchCause((cause) => appendProviderFailureActivity({
|
|
292809
|
+
threadId: event.payload.threadId,
|
|
292810
|
+
kind: "provider.session.stop.failed",
|
|
292811
|
+
summary: "Provider session stop failed",
|
|
292812
|
+
detail: publicProviderFailureDetail(pretty(cause)),
|
|
292813
|
+
turnId: null,
|
|
292814
|
+
createdAt: now
|
|
292815
|
+
})));
|
|
292464
292816
|
});
|
|
292465
292817
|
const processDomainEvent = fn$1("processDomainEvent")(function* (event) {
|
|
292466
292818
|
yield* annotateCurrentSpan({
|
|
@@ -292555,7 +292907,7 @@ const ProviderCommandReactorLive = effect(ProviderCommandReactor, gen(function*
|
|
|
292555
292907
|
threadId: event.payload.threadId,
|
|
292556
292908
|
kind: "provider.turn.start.failed",
|
|
292557
292909
|
summary: "Provider turn start cleanup failed",
|
|
292558
|
-
detail:
|
|
292910
|
+
detail: publicProviderFailureDetailFromError(cause),
|
|
292559
292911
|
turnId: null,
|
|
292560
292912
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
292561
292913
|
})
|
|
@@ -297975,7 +298327,7 @@ function resolveBuildCommitFromEnv(env) {
|
|
|
297975
298327
|
* environment descriptor down instead of reporting an honest "unknown".
|
|
297976
298328
|
*/
|
|
297977
298329
|
function readBakedBuildCommit() {
|
|
297978
|
-
return "
|
|
298330
|
+
return "b06001f4e16f7a336e9108920f9250b366862f3d";
|
|
297979
298331
|
}
|
|
297980
298332
|
async function resolveServerBuildCommit(input) {
|
|
297981
298333
|
if (isFullCommitSha(input.baked)) return input.baked;
|
|
@@ -309327,6 +309679,33 @@ registerProxyRoute({
|
|
|
309327
309679
|
cmmdPath: "/api/projects/campaigns/:id",
|
|
309328
309680
|
orgIdSource: "none"
|
|
309329
309681
|
});
|
|
309682
|
+
/**
|
|
309683
|
+
* Apple App Store guideline 5.1.1(v): an app that supports account creation
|
|
309684
|
+
* or sign-in must let the user start account deletion from inside the app.
|
|
309685
|
+
* CMMD owns the account, so this proxies to its existing deactivation route.
|
|
309686
|
+
*
|
|
309687
|
+
* `cmmdPath` is `/api/settings/delete-account`, not a `/api/forge/context/*`
|
|
309688
|
+
* route: CMMD never built a forge-context-scoped deletion endpoint. It is
|
|
309689
|
+
* still reachable through the same owner JWT this gateway already resolves
|
|
309690
|
+
* per session: CMMD's `optionalJwtAuth` runs ahead of every `/api/*` route
|
|
309691
|
+
* (not only forge-context ones), so the same Bearer that reaches
|
|
309692
|
+
* `/api/cmmd/bootstrap` also authenticates here.
|
|
309693
|
+
*
|
|
309694
|
+
* CMMD's route soft-deletes: it flips the user to `active: false` and
|
|
309695
|
+
* `lifecycleState: 'deactivated'`, revokes CMMD's own refresh tokens, and
|
|
309696
|
+
* destroys its session. It does not purge data on this call. The Forge
|
|
309697
|
+
* client is responsible for the rest: revoking the Forge session
|
|
309698
|
+
* (`/api/auth/logout`) and clearing local state, the same as a manual
|
|
309699
|
+
* sign-out.
|
|
309700
|
+
*/
|
|
309701
|
+
registerProxyRoute({
|
|
309702
|
+
forgePath: "/api/cmmd/settings/delete-account",
|
|
309703
|
+
method: "DELETE",
|
|
309704
|
+
cmmdPath: "/api/settings/delete-account",
|
|
309705
|
+
orgIdSource: "none",
|
|
309706
|
+
responseShape: "unwrapData",
|
|
309707
|
+
requestBody: "none"
|
|
309708
|
+
});
|
|
309330
309709
|
registerProxyRoute({
|
|
309331
309710
|
forgePath: "/api/cmmd/bootstrap",
|
|
309332
309711
|
method: "GET",
|
|
@@ -321315,7 +321694,7 @@ const PlatformServicesLive = unwrap(gen(function* () {
|
|
|
321315
321694
|
}));
|
|
321316
321695
|
const RuntimeReplicationLayerLive = RuntimeReplicationLive.pipe(provideMerge(RuntimeReplicationClientLive), provideMerge(RuntimeReplicationOutboxLive), provideMerge(ActiveHourMeterLive.pipe(provide$1(RuntimeReplicationOutboxLive))));
|
|
321317
321696
|
const RuntimeDrainLayerLive = RuntimeDrainReporterLive.pipe(provideMerge(RuntimeDrainStateLive));
|
|
321318
|
-
const RuntimeAwareProviderReactorsLive = mergeAll(ProviderRuntimeIngestionLive, ProviderCommandReactorLive).pipe(provide$1(RuntimeDrainLayerLive), provide$1(ClaudeUsageTelemetryLive));
|
|
321697
|
+
const RuntimeAwareProviderReactorsLive = mergeAll(ProviderRuntimeIngestionLive, ProviderCommandReactorLive).pipe(provide$1(RuntimeDrainLayerLive), provideMerge(RuntimeDrainStateLive), provide$1(ClaudeUsageTelemetryLive));
|
|
321319
321698
|
const PipelineWorkerCoordinatorLayerLive = PipelineWorkerCoordinatorLive.pipe(provideMerge(PipelineRunClientLive));
|
|
321320
321699
|
const AgentProtocolReactorLayerLive = AgentProtocolReactorLive.pipe(provideMerge(PipelineRunStoreLive));
|
|
321321
321700
|
const PipelineRunCoordinatorLayerLive = PipelineRunCoordinatorLive.pipe(provideMerge(PipelineProviderAuthorityInstallerLive), provideMerge(PipelineChangeInspectorLive), provideMerge(PipelineContextArtifactStoreLive), provideMerge(PipelineModelPolicyLive), provideMerge(PipelineRunClientLive), provideMerge(PipelineRunStoreLive), provideMerge(PipelineRunWorkspaceAllocatorLive), provideMerge(PipelineStagePolicyLive), provideMerge(PipelineWorkerCoordinatorLayerLive));
|