@lore-co/cli 0.2.19 → 0.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -2
- package/dist/api.d.ts +4 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +11 -3
- package/dist/api.js.map +1 -1
- package/dist/approach-guidance.d.ts.map +1 -1
- package/dist/approach-guidance.js +2 -1
- package/dist/approach-guidance.js.map +1 -1
- package/dist/approach.d.ts +1 -1
- package/dist/approach.d.ts.map +1 -1
- package/dist/approach.js +12 -4
- package/dist/approach.js.map +1 -1
- package/dist/ask.d.ts +9 -1
- package/dist/ask.d.ts.map +1 -1
- package/dist/ask.js +32 -3
- package/dist/ask.js.map +1 -1
- package/dist/cli.d.ts +36 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +225 -48
- package/dist/cli.js.map +1 -1
- package/dist/command-help.d.ts +3 -1
- package/dist/command-help.d.ts.map +1 -1
- package/dist/command-help.js +13 -1
- package/dist/command-help.js.map +1 -1
- package/dist/devin-cli.js +2 -2
- package/dist/devin-cli.js.map +1 -1
- package/dist/embeddings.d.ts +12 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +105 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/generated-assets.d.ts +5 -4
- package/dist/generated-assets.d.ts.map +1 -1
- package/dist/generated-assets.js +5 -4
- package/dist/generated-assets.js.map +1 -1
- package/dist/github.d.ts.map +1 -1
- package/dist/github.js +242 -4
- package/dist/github.js.map +1 -1
- package/dist/guard.d.ts.map +1 -1
- package/dist/guard.js +4 -6
- package/dist/guard.js.map +1 -1
- package/dist/handoff.js +2 -2
- package/dist/handoff.js.map +1 -1
- package/dist/learnings.d.ts +31 -0
- package/dist/learnings.d.ts.map +1 -0
- package/dist/learnings.js +265 -0
- package/dist/learnings.js.map +1 -0
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +96 -11
- package/dist/live.js.map +1 -1
- package/dist/platform.d.ts +7 -0
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +25 -1
- package/dist/platform.js.map +1 -1
- package/dist/recovery-state.js +1 -1
- package/dist/recovery-state.js.map +1 -1
- package/dist/reliability-store.d.ts +31 -0
- package/dist/reliability-store.d.ts.map +1 -1
- package/dist/reliability-store.js +89 -0
- package/dist/reliability-store.js.map +1 -1
- package/dist/retrieval-task.d.ts.map +1 -1
- package/dist/retrieval-task.js +2 -11
- package/dist/retrieval-task.js.map +1 -1
- package/dist/runtime.d.ts +13 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +124 -33
- package/dist/runtime.js.map +1 -1
- package/dist/self-host.d.ts +2 -2
- package/dist/self-host.d.ts.map +1 -1
- package/dist/self-host.js +4 -3
- package/dist/self-host.js.map +1 -1
- package/dist/skill-guidance.js +1 -1
- package/dist/skill-guidance.js.map +1 -1
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +24 -4
- package/dist/skills.js.map +1 -1
- package/dist/uninstall.d.ts.map +1 -1
- package/dist/uninstall.js +4 -3
- package/dist/uninstall.js.map +1 -1
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +4 -23
- package/dist/update.js.map +1 -1
- package/package.json +3 -3
package/dist/runtime.js
CHANGED
|
@@ -13,7 +13,7 @@ import { dirname, parse, relative, resolve } from "node:path";
|
|
|
13
13
|
import { homedir } from "node:os";
|
|
14
14
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
15
15
|
import { promisify } from "node:util";
|
|
16
|
-
import { boundedUtf8Text, defaultGitPushBranches, repositoryScopeFromGitRoot, } from "./repository.js";
|
|
16
|
+
import { boundedUtf8Text, defaultGitPushBranches, repositoryRemoteScope, repositoryScopeFromGitRoot, } from "./repository.js";
|
|
17
17
|
import { ReliabilityStore, ReliabilityStoreError, atomicWriteJson as durableAtomicWriteJson, classifyRetryFailure, createIntegrationInvocationAttempt, durableUnlink, recordLocalGuardMetric, recordLocalRetrievalMetric, } from "./reliability-store.js";
|
|
18
18
|
import { writeInvocationAttemptBounded, writeInvocationCompletionBounded, } from "./invocation-health-writer.js";
|
|
19
19
|
import { SignedSnapshotError, verifySignedSnapshot, } from "./signed-snapshot.js";
|
|
@@ -21,7 +21,7 @@ import { CONTEXT_CACHE_MAX_STALE_MS, isContextTransportFailure, selectCachedCont
|
|
|
21
21
|
import { RUNTIME_VERSION } from "./runtime-version.js";
|
|
22
22
|
import { checkLoreAccess, checkDirectory, checkWorkspaceRepository, readLocalConfig, resolveLocalConnection, bindLocalSession } from "./directory-policy.js";
|
|
23
23
|
import { capturePendingImages, enqueueEventImages, flushImageAttachment, hostMessageImages, rememberImageCapture } from "./image-capture.js";
|
|
24
|
-
import { guardRuleConflict, guardToolTrigger, supportedGuardRule, guardEmergencyCommand, emergencyOverrideApplies } from "@lore-co/core/guard-evaluation";
|
|
24
|
+
import { guardRepositoryOverride, guardRuleConflict, guardToolTrigger, supportedGuardRule, guardEmergencyCommand, emergencyOverrideApplies } from "@lore-co/core/guard-evaluation";
|
|
25
25
|
const IS_STANDALONE_RUNTIME = typeof __LORE_STANDALONE__ === "boolean" && __LORE_STANDALONE__;
|
|
26
26
|
export const COMMAND_HOOK_AGENT_NAMES = [
|
|
27
27
|
"codex",
|
|
@@ -456,7 +456,12 @@ async function repositoryRoot(cwd) {
|
|
|
456
456
|
}
|
|
457
457
|
}
|
|
458
458
|
}
|
|
459
|
-
|
|
459
|
+
/**
|
|
460
|
+
* Captured scope records only an origin-derived repository, so a checkout
|
|
461
|
+
* without a remote never stores its folder name as one. Guard keeps the folder
|
|
462
|
+
* label for that case because local overrides are keyed by it.
|
|
463
|
+
*/
|
|
464
|
+
async function repositoryScope(cwd, repository = repositoryRemoteScope) {
|
|
460
465
|
if (cwd === undefined) {
|
|
461
466
|
return undefined;
|
|
462
467
|
}
|
|
@@ -465,8 +470,9 @@ async function repositoryScope(cwd) {
|
|
|
465
470
|
return undefined;
|
|
466
471
|
}
|
|
467
472
|
const scopedPath = normalizedRepositoryPath(relative(root, resolve(cwd)));
|
|
473
|
+
const repo = await repository(root);
|
|
468
474
|
return {
|
|
469
|
-
repo:
|
|
475
|
+
...(repo === undefined ? {} : { repo }),
|
|
470
476
|
...(scopedPath === "" ? {} : { path: scopedPath }),
|
|
471
477
|
};
|
|
472
478
|
}
|
|
@@ -485,20 +491,16 @@ async function gitOutput(root, args) {
|
|
|
485
491
|
}
|
|
486
492
|
async function collectGitContext(cwd) {
|
|
487
493
|
const scope = await repositoryScope(cwd);
|
|
488
|
-
if (cwd === undefined || scope
|
|
494
|
+
if (cwd === undefined || scope === undefined) {
|
|
489
495
|
return {
|
|
490
496
|
...(scope === undefined ? {} : { scope }),
|
|
491
497
|
learningScope: {},
|
|
492
498
|
};
|
|
493
499
|
}
|
|
500
|
+
const learningScope = scope.repo === undefined ? {} : { repo: scope.repo };
|
|
494
501
|
const root = await repositoryRoot(cwd);
|
|
495
502
|
if (root === null) {
|
|
496
|
-
return {
|
|
497
|
-
scope,
|
|
498
|
-
learningScope: {
|
|
499
|
-
repo: scope.repo,
|
|
500
|
-
},
|
|
501
|
-
};
|
|
503
|
+
return { scope, learningScope };
|
|
502
504
|
}
|
|
503
505
|
const [trackedFiles, untrackedFiles, rawDiff] = await Promise.all([
|
|
504
506
|
gitOutput(root, [
|
|
@@ -527,9 +529,7 @@ async function collectGitContext(cwd) {
|
|
|
527
529
|
const diff = boundedUtf8Text(redactSecrets(rawDiff), MAX_GIT_DIFF_BYTES, "\n\n[diff truncated by Lore]").trim();
|
|
528
530
|
return {
|
|
529
531
|
scope,
|
|
530
|
-
learningScope
|
|
531
|
-
repo: scope.repo,
|
|
532
|
-
},
|
|
532
|
+
learningScope,
|
|
533
533
|
...(files.length === 0 ? {} : { files }),
|
|
534
534
|
...(diff === "" ? {} : { diff }),
|
|
535
535
|
};
|
|
@@ -813,6 +813,64 @@ function deliveryFromResponse(value) {
|
|
|
813
813
|
}
|
|
814
814
|
class IncompatibleReliabilityError extends Error {
|
|
815
815
|
incompatible = true;
|
|
816
|
+
reason;
|
|
817
|
+
constructor(message, reason = "invalid_response") {
|
|
818
|
+
super(message);
|
|
819
|
+
this.reason = reason;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
const RETRIEVAL_FAILURE_STAGE = Symbol("lore.retrievalFailureStage");
|
|
823
|
+
function markKeyRefreshFailure(error) {
|
|
824
|
+
if (typeof error === "object" && error !== null) {
|
|
825
|
+
Object.defineProperty(error, RETRIEVAL_FAILURE_STAGE, {
|
|
826
|
+
value: "key_refresh",
|
|
827
|
+
enumerable: false,
|
|
828
|
+
configurable: true,
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
return error;
|
|
832
|
+
}
|
|
833
|
+
export function classifyRetrievalFailure(error) {
|
|
834
|
+
if (typeof error === "object" &&
|
|
835
|
+
error !== null &&
|
|
836
|
+
error[RETRIEVAL_FAILURE_STAGE] === "key_refresh") {
|
|
837
|
+
return "key_refresh";
|
|
838
|
+
}
|
|
839
|
+
if (error instanceof IncompatibleReliabilityError) {
|
|
840
|
+
return error.reason;
|
|
841
|
+
}
|
|
842
|
+
if (error instanceof SignedSnapshotError) {
|
|
843
|
+
return "snapshot_unverified";
|
|
844
|
+
}
|
|
845
|
+
if (error instanceof RuntimeHttpError) {
|
|
846
|
+
return `http_${error.httpStatus}`;
|
|
847
|
+
}
|
|
848
|
+
if (error instanceof Error) {
|
|
849
|
+
if (["AbortError", "TimeoutError", "LoreTimeoutError"].includes(error.name)) {
|
|
850
|
+
return "timeout";
|
|
851
|
+
}
|
|
852
|
+
const own = error.code;
|
|
853
|
+
const cause = error.cause;
|
|
854
|
+
const code = typeof own === "string"
|
|
855
|
+
? own
|
|
856
|
+
: isObject(cause) && typeof cause.code === "string"
|
|
857
|
+
? cause.code
|
|
858
|
+
: undefined;
|
|
859
|
+
if ((code !== undefined && isContextTransportFailure({ code })) ||
|
|
860
|
+
(code === undefined && error instanceof TypeError)) {
|
|
861
|
+
return "network";
|
|
862
|
+
}
|
|
863
|
+
const status = error;
|
|
864
|
+
const httpStatus = typeof status.httpStatus === "number"
|
|
865
|
+
? status.httpStatus
|
|
866
|
+
: typeof status.status === "number"
|
|
867
|
+
? status.status
|
|
868
|
+
: undefined;
|
|
869
|
+
if (httpStatus !== undefined && Number.isInteger(httpStatus)) {
|
|
870
|
+
return `http_${httpStatus}`;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
return "unknown";
|
|
816
874
|
}
|
|
817
875
|
async function protocolResponseJson(response) {
|
|
818
876
|
try {
|
|
@@ -853,7 +911,7 @@ function deliveryFromSignedContext(payload, expectedRequest) {
|
|
|
853
911
|
!Array.isArray(payload.memories) ||
|
|
854
912
|
!Array.isArray(payload.hits) ||
|
|
855
913
|
!isObject(payload.packing)) {
|
|
856
|
-
throw new IncompatibleReliabilityError("Lore context snapshot does not bind the request or signed delivery");
|
|
914
|
+
throw new IncompatibleReliabilityError("Lore context snapshot does not bind the request or signed delivery", "binding_mismatch");
|
|
857
915
|
}
|
|
858
916
|
return deliveryFromResponse({
|
|
859
917
|
context: payload.context,
|
|
@@ -1075,15 +1133,29 @@ async function verifySnapshot(compactJws, kind, config, store, fetchImplementati
|
|
|
1075
1133
|
keys,
|
|
1076
1134
|
allowExpired,
|
|
1077
1135
|
}).payload;
|
|
1136
|
+
let keys;
|
|
1137
|
+
try {
|
|
1138
|
+
keys = await pinnedKeys(config, store, fetchImplementation);
|
|
1139
|
+
}
|
|
1140
|
+
catch (error) {
|
|
1141
|
+
throw markKeyRefreshFailure(error);
|
|
1142
|
+
}
|
|
1078
1143
|
try {
|
|
1079
|
-
return verifyWith(
|
|
1144
|
+
return verifyWith(keys);
|
|
1080
1145
|
}
|
|
1081
1146
|
catch (error) {
|
|
1082
1147
|
if (!(error instanceof SignedSnapshotError) ||
|
|
1083
1148
|
error.code !== "UNTRUSTED_KEY") {
|
|
1084
1149
|
throw error;
|
|
1085
1150
|
}
|
|
1086
|
-
|
|
1151
|
+
let refreshed;
|
|
1152
|
+
try {
|
|
1153
|
+
refreshed = await refreshTrustKeys(config, store, fetchImplementation);
|
|
1154
|
+
}
|
|
1155
|
+
catch (refreshError) {
|
|
1156
|
+
throw markKeyRefreshFailure(refreshError);
|
|
1157
|
+
}
|
|
1158
|
+
return verifyWith(refreshed);
|
|
1087
1159
|
}
|
|
1088
1160
|
}
|
|
1089
1161
|
function committedAcknowledgement(value, externalEventId, serverEventId) {
|
|
@@ -1136,13 +1208,13 @@ async function postTurn(config, request, fetchImplementation, store) {
|
|
|
1136
1208
|
typeof value.turn.event.id !== "string" ||
|
|
1137
1209
|
!isObject(value.retrieval) ||
|
|
1138
1210
|
!committedAcknowledgement(value.capture, request.eventId, value.turn.event.id)) {
|
|
1139
|
-
throw new IncompatibleReliabilityError("Lore turn response has no matching durable acknowledgement");
|
|
1211
|
+
throw new IncompatibleReliabilityError("Lore turn response has no matching durable acknowledgement", "acknowledgement_mismatch");
|
|
1140
1212
|
}
|
|
1141
1213
|
const payload = await verifySnapshot(value.contextSnapshot, "context", config, store, fetchImplementation, true);
|
|
1142
1214
|
if (payload.eventId !== value.turn.event.id ||
|
|
1143
1215
|
!isObject(value.turn.receipt) ||
|
|
1144
1216
|
payload.receiptId !== value.turn.receipt.id) {
|
|
1145
|
-
throw new IncompatibleReliabilityError("Lore turn snapshot does not bind the returned event and receipt");
|
|
1217
|
+
throw new IncompatibleReliabilityError("Lore turn snapshot does not bind the returned event and receipt", "binding_mismatch");
|
|
1146
1218
|
}
|
|
1147
1219
|
const reliability = retrievalReliability(value.retrieval, payload);
|
|
1148
1220
|
if (Date.now() - Date.parse(payload.validUntil) >
|
|
@@ -1172,7 +1244,8 @@ async function postTurn(config, request, fetchImplementation, store) {
|
|
|
1172
1244
|
task: {
|
|
1173
1245
|
agent: request.agent,
|
|
1174
1246
|
...(request.scope === undefined ? {} : { scope: request.scope }),
|
|
1175
|
-
|
|
1247
|
+
// The server trims the retrieval task before searching and signing.
|
|
1248
|
+
task: (request.task ?? request.currentUser.content).trim(),
|
|
1176
1249
|
},
|
|
1177
1250
|
});
|
|
1178
1251
|
await store
|
|
@@ -1287,7 +1360,7 @@ async function getPromptContext(config, agent, sessionId, sourceEventId, prompt,
|
|
|
1287
1360
|
const payload = await verifySnapshot(value.snapshot, "context", config, store, fetchImplementation, true);
|
|
1288
1361
|
if (payload.eventId !== value.delivery.event.id ||
|
|
1289
1362
|
payload.receiptId !== value.delivery.receipt.id) {
|
|
1290
|
-
throw new IncompatibleReliabilityError("Lore context snapshot does not bind the returned event and receipt");
|
|
1363
|
+
throw new IncompatibleReliabilityError("Lore context snapshot does not bind the returned event and receipt", "binding_mismatch");
|
|
1291
1364
|
}
|
|
1292
1365
|
const reliability = retrievalReliability(value.reliability, payload);
|
|
1293
1366
|
if (Date.now() - Date.parse(payload.validUntil) >
|
|
@@ -1399,11 +1472,16 @@ async function getCachedPromptContext(config, prompt, gitContext, store, now, re
|
|
|
1399
1472
|
},
|
|
1400
1473
|
};
|
|
1401
1474
|
}
|
|
1402
|
-
async function getPromptContextWithFallback(config, agent, sessionId, sourceEventId, prompt, gitContext, fetchImplementation, store, now, retained = []) {
|
|
1475
|
+
async function getPromptContextWithFallback(config, agent, sessionId, sourceEventId, prompt, gitContext, fetchImplementation, store, now, retained = [], failures) {
|
|
1403
1476
|
try {
|
|
1404
1477
|
return await getPromptContext(config, agent, sessionId, sourceEventId, prompt, gitContext, fetchImplementation, store, retained);
|
|
1405
1478
|
}
|
|
1406
1479
|
catch (error) {
|
|
1480
|
+
failures?.push({
|
|
1481
|
+
reason: classifyRetrievalFailure(error),
|
|
1482
|
+
agent,
|
|
1483
|
+
path: "context",
|
|
1484
|
+
});
|
|
1407
1485
|
if (isContextTransportFailure(error)) {
|
|
1408
1486
|
const cached = await getCachedPromptContext(config, prompt, gitContext, store, now, retained).catch(() => null);
|
|
1409
1487
|
if (cached !== null) {
|
|
@@ -1623,6 +1701,7 @@ async function flushOne(config, fetchImplementation, store, preferredIdempotency
|
|
|
1623
1701
|
: failed.state === "dead"
|
|
1624
1702
|
? "dead"
|
|
1625
1703
|
: "ready",
|
|
1704
|
+
failureReason: classifyRetrievalFailure(error),
|
|
1626
1705
|
};
|
|
1627
1706
|
}
|
|
1628
1707
|
}
|
|
@@ -1885,7 +1964,7 @@ async function offlineGuard(config, store, request, now, previouslyEnforced, all
|
|
|
1885
1964
|
return { offline: true, mode: "enforce", requiresConfirmation: false, items: [], conflictSummaries: ["An audited, time-limited emergency override is active for this session and repository."] };
|
|
1886
1965
|
}
|
|
1887
1966
|
const overrides = isObject(remembered) && Array.isArray(remembered.overrides) ? remembered.overrides : [];
|
|
1888
|
-
const override = overrides
|
|
1967
|
+
const override = guardRepositoryOverride(overrides, request.repo);
|
|
1889
1968
|
const knownEnforce = previouslyEnforced || (isObject(override) ? override.mode : isObject(remembered) ? remembered.mode : undefined) === "enforce";
|
|
1890
1969
|
try {
|
|
1891
1970
|
if (!allowCachedPolicy)
|
|
@@ -1897,7 +1976,7 @@ async function offlineGuard(config, store, request, now, previouslyEnforced, all
|
|
|
1897
1976
|
const payload = verifySignedSnapshot(compactJws, {
|
|
1898
1977
|
workspaceId: store.workspaceId, keys, kind: "guard_policy", now,
|
|
1899
1978
|
}).payload;
|
|
1900
|
-
const cachedOverride =
|
|
1979
|
+
const cachedOverride = guardRepositoryOverride(payload.overrides, request.repo);
|
|
1901
1980
|
const mode = isObject(cachedOverride) ? cachedOverride.mode : payload.mode;
|
|
1902
1981
|
if (isObject(remembered) && remembered.policyVersion !== payload.policyVersion)
|
|
1903
1982
|
throw new Error("Policy has changed");
|
|
@@ -1956,7 +2035,7 @@ async function handleGuardEvent(input, agent, config, sessionId, options, now) {
|
|
|
1956
2035
|
return undefined;
|
|
1957
2036
|
}
|
|
1958
2037
|
const cwd = stringField(input.cwd);
|
|
1959
|
-
const scope = await repositoryScope(cwd);
|
|
2038
|
+
const scope = await repositoryScope(cwd, repositoryScopeFromGitRoot);
|
|
1960
2039
|
const root = cwd === undefined ? null : await repositoryRoot(cwd);
|
|
1961
2040
|
const files = (trigger.files ?? [])
|
|
1962
2041
|
.map((file) => {
|
|
@@ -2128,8 +2207,8 @@ export async function handleHookEvent(value, agent, options = {}) {
|
|
|
2128
2207
|
const prior = await readGuardState(agent, sessionId, options.home, config.connectionId).catch(() => null);
|
|
2129
2208
|
const saved = await runtimeStore(config, options.home).then(store => store.readState("guard-policy-state")).catch(() => null);
|
|
2130
2209
|
// Use the same scope as live Guard: non-Git folders have no repo override.
|
|
2131
|
-
const scope = await repositoryScope(stringField(input.cwd)).catch(() => undefined);
|
|
2132
|
-
const override = isObject(saved)
|
|
2210
|
+
const scope = await repositoryScope(stringField(input.cwd), repositoryScopeFromGitRoot).catch(() => undefined);
|
|
2211
|
+
const override = guardRepositoryOverride(isObject(saved) ? saved.overrides : undefined, scope?.repo);
|
|
2133
2212
|
if (prior?.mode === "enforce" || (isObject(override) ? override.mode : isObject(saved) ? saved.mode : undefined) === "enforce") {
|
|
2134
2213
|
const reason = "Lore cannot verify workspace repository settings. Restore the connection before retrying.";
|
|
2135
2214
|
if (agent === "cursor")
|
|
@@ -2187,8 +2266,8 @@ export async function handleHookEvent(value, agent, options = {}) {
|
|
|
2187
2266
|
catch {
|
|
2188
2267
|
const prior = await readGuardState(agent, sessionId, options.home, config.connectionId).catch(() => null);
|
|
2189
2268
|
const saved = await runtimeStore(config, options.home).then((store) => store.readState("guard-policy-state")).catch(() => null);
|
|
2190
|
-
const scope = await repositoryScope(stringField(input.cwd)).catch(() => undefined);
|
|
2191
|
-
const override = isObject(saved)
|
|
2269
|
+
const scope = await repositoryScope(stringField(input.cwd), repositoryScopeFromGitRoot).catch(() => undefined);
|
|
2270
|
+
const override = guardRepositoryOverride(isObject(saved) ? saved.overrides : undefined, scope?.repo);
|
|
2192
2271
|
if (prior?.mode === "enforce" || (isObject(override) ? override.mode : isObject(saved) ? saved.mode : undefined) === "enforce") {
|
|
2193
2272
|
const reason = "Lore Guard cannot verify its enforcement policy. Restore Lore before retrying.";
|
|
2194
2273
|
if (agent === "cursor")
|
|
@@ -2288,6 +2367,8 @@ async function handlePromptEvent(input, agent, sessionId, config, options, now,
|
|
|
2288
2367
|
throw new InvalidHookInputError();
|
|
2289
2368
|
}
|
|
2290
2369
|
const notices = [];
|
|
2370
|
+
// Classified reasons only: never the prompt, task, or request payload.
|
|
2371
|
+
const retrievalFailures = [];
|
|
2291
2372
|
let store;
|
|
2292
2373
|
try {
|
|
2293
2374
|
store = await runtimeStore(config, options.home);
|
|
@@ -2402,8 +2483,15 @@ async function handlePromptEvent(input, agent, sessionId, config, options, now,
|
|
|
2402
2483
|
else if (!terminal(flushed, request.eventId)) {
|
|
2403
2484
|
notices.push("Lore saved this capture locally; server upload is pending.");
|
|
2404
2485
|
}
|
|
2486
|
+
if (retrieve &&
|
|
2487
|
+
flushed?.idempotencyKey === request.eventId &&
|
|
2488
|
+
flushed.failureReason !== undefined) {
|
|
2489
|
+
retrievalFailures.push({ reason: flushed.failureReason, agent, path: "turn" });
|
|
2490
|
+
}
|
|
2405
2491
|
}
|
|
2406
2492
|
catch {
|
|
2493
|
+
// Local store errors are not retrieval failures; the context
|
|
2494
|
+
// delivery below records its own reason if it also fails.
|
|
2407
2495
|
notices.push("Lore saved this capture locally; server upload is pending.");
|
|
2408
2496
|
}
|
|
2409
2497
|
}
|
|
@@ -2414,9 +2502,10 @@ async function handlePromptEvent(input, agent, sessionId, config, options, now,
|
|
|
2414
2502
|
learningScope: request.learningScope,
|
|
2415
2503
|
...(request.diff === undefined ? {} : { diff: request.diff }),
|
|
2416
2504
|
...(request.files === undefined ? {} : { files: request.files }),
|
|
2417
|
-
}, fetchImplementation, store, now, retained);
|
|
2505
|
+
}, fetchImplementation, store, now, retained, retrievalFailures);
|
|
2418
2506
|
}
|
|
2419
|
-
catch {
|
|
2507
|
+
catch (error) {
|
|
2508
|
+
retrievalFailures.push({ reason: classifyRetrievalFailure(error), agent, path: "context" });
|
|
2420
2509
|
delivery = {
|
|
2421
2510
|
...emptyDelivery(),
|
|
2422
2511
|
reliability: {
|
|
@@ -2476,9 +2565,10 @@ async function handlePromptEvent(input, agent, sessionId, config, options, now,
|
|
|
2476
2565
|
}
|
|
2477
2566
|
if (retrieve) {
|
|
2478
2567
|
try {
|
|
2479
|
-
delivery = await getPromptContextWithFallback(config, agent, sessionId, observation.eventId, task, gitContext, fetchImplementation, store, now, retained);
|
|
2568
|
+
delivery = await getPromptContextWithFallback(config, agent, sessionId, observation.eventId, task, gitContext, fetchImplementation, store, now, retained, retrievalFailures);
|
|
2480
2569
|
}
|
|
2481
|
-
catch {
|
|
2570
|
+
catch (error) {
|
|
2571
|
+
retrievalFailures.push({ reason: classifyRetrievalFailure(error), agent, path: "context" });
|
|
2482
2572
|
delivery = {
|
|
2483
2573
|
...emptyDelivery(),
|
|
2484
2574
|
reliability: {
|
|
@@ -2513,6 +2603,7 @@ async function handlePromptEvent(input, agent, sessionId, config, options, now,
|
|
|
2513
2603
|
...(reliability?.freshness.ageMs === undefined
|
|
2514
2604
|
? {}
|
|
2515
2605
|
: { cacheAgeMs: reliability.freshness.ageMs }),
|
|
2606
|
+
...(retrievalFailures.length === 0 ? {} : { failures: retrievalFailures }),
|
|
2516
2607
|
}, now).catch(() => undefined);
|
|
2517
2608
|
const contextReliabilityNotice = await retrievalNotice(delivery, sessionId, store, now);
|
|
2518
2609
|
if (contextReliabilityNotice !== undefined) {
|