@lore-co/cli 0.2.14 → 0.2.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/README.md +78 -4
- package/dist/capture-scope.d.ts +12 -0
- package/dist/capture-scope.d.ts.map +1 -0
- package/dist/capture-scope.js +45 -0
- package/dist/capture-scope.js.map +1 -0
- package/dist/cli.d.ts +7 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +126 -38
- package/dist/cli.js.map +1 -1
- package/dist/command-help.d.ts +2 -1
- package/dist/command-help.d.ts.map +1 -1
- package/dist/command-help.js +4 -1
- package/dist/command-help.js.map +1 -1
- package/dist/connect-project.d.ts +15 -0
- package/dist/connect-project.d.ts.map +1 -0
- package/dist/connect-project.js +87 -0
- package/dist/connect-project.js.map +1 -0
- package/dist/connections.d.ts +3 -1
- package/dist/connections.d.ts.map +1 -1
- package/dist/connections.js +6 -1
- package/dist/connections.js.map +1 -1
- package/dist/devin-cli.d.ts.map +1 -1
- package/dist/devin-cli.js +14 -6
- package/dist/devin-cli.js.map +1 -1
- package/dist/devin.d.ts.map +1 -1
- package/dist/devin.js +5 -2
- package/dist/devin.js.map +1 -1
- 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/host.d.ts.map +1 -1
- package/dist/host.js +5 -5
- package/dist/host.js.map +1 -1
- package/dist/reliability-store.d.ts +5 -0
- package/dist/reliability-store.d.ts.map +1 -1
- package/dist/reliability-store.js +9 -1
- package/dist/reliability-store.js.map +1 -1
- package/dist/remember.d.ts.map +1 -1
- package/dist/remember.js +8 -1
- package/dist/remember.js.map +1 -1
- package/dist/retrieval-task.d.ts +20 -0
- package/dist/retrieval-task.d.ts.map +1 -0
- package/dist/retrieval-task.js +78 -0
- package/dist/retrieval-task.js.map +1 -0
- package/dist/runtime-redaction.d.ts +2 -0
- package/dist/runtime-redaction.d.ts.map +1 -0
- package/dist/runtime-redaction.js +69 -0
- package/dist/runtime-redaction.js.map +1 -0
- package/dist/runtime.d.ts +2 -2
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +129 -89
- package/dist/runtime.js.map +1 -1
- package/dist/self-host.d.ts +2 -1
- 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/signed-snapshot.d.ts.map +1 -1
- package/dist/signed-snapshot.js +2 -0
- package/dist/signed-snapshot.js.map +1 -1
- package/dist/skill-command.d.ts +20 -0
- package/dist/skill-command.d.ts.map +1 -0
- package/dist/skill-command.js +215 -0
- package/dist/skill-command.js.map +1 -0
- package/dist/skill-guidance.d.ts +2 -0
- package/dist/skill-guidance.d.ts.map +1 -0
- package/dist/skill-guidance.js +7 -0
- package/dist/skill-guidance.js.map +1 -0
- package/dist/skills.d.ts +16 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +126 -0
- package/dist/skills.js.map +1 -0
- package/dist/update.js +3 -3
- package/package.json +7 -3
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const NON_SECRET_TOKEN_VALUES = new Set([
|
|
2
|
+
"available",
|
|
3
|
+
"configured",
|
|
4
|
+
"generated",
|
|
5
|
+
"optional",
|
|
6
|
+
"placeholder",
|
|
7
|
+
"redacted",
|
|
8
|
+
"required",
|
|
9
|
+
"undefined",
|
|
10
|
+
]);
|
|
11
|
+
function redactedCredential(value) {
|
|
12
|
+
// Preserve markers from earlier token rules and repeated redaction passes.
|
|
13
|
+
if (/^\[REDACTED(?::[A-Z_]+|_[A-Z_]+)?\][.,!?]*$/u.test(value)) {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
const trailing = /[.,!?]+$/u.exec(value)?.[0] ?? "";
|
|
17
|
+
return `[REDACTED:CREDENTIAL]${trailing}`;
|
|
18
|
+
}
|
|
19
|
+
// This standalone runtime cannot import @lore-co/core after it is copied to
|
|
20
|
+
// ~/.lore. Keep these rules aligned with core/redaction.ts through parity tests.
|
|
21
|
+
// Match invisible characters inside tokens without changing ordinary text;
|
|
22
|
+
// real whitespace still separates token bodies and adjacent lines.
|
|
23
|
+
const TOKEN_GAP = "(?:\\p{Cf}|(?!\\s)\\p{Cc})*";
|
|
24
|
+
const tokenPrefix = (prefix) => Array.from(prefix, (character) => character.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")).join(TOKEN_GAP);
|
|
25
|
+
const tokenBody = (characters, minimum, maximum) => `${characters}(?:${TOKEN_GAP}${characters}){${minimum - 1},${maximum === undefined ? "" : maximum - 1}}`;
|
|
26
|
+
const providerToken = (prefix, characters, minimum, maximum) => tokenPrefix(prefix) + TOKEN_GAP + tokenBody(characters, minimum, maximum);
|
|
27
|
+
const AUTHORIZATION_TOKEN_PATTERN = new RegExp([
|
|
28
|
+
`\\b(${tokenPrefix("Bearer")}|${tokenPrefix("Basic")})`,
|
|
29
|
+
"[\\s\\p{Cc}\\p{Cf}]+",
|
|
30
|
+
tokenBody(/[A-Za-z0-9._~+\/=-]/u.source, 8),
|
|
31
|
+
].join(""), "giu");
|
|
32
|
+
const JWT_TOKEN_PATTERN = new RegExp(`\\b${tokenPrefix("eyJ")}${TOKEN_GAP}${tokenBody("[A-Za-z0-9_-]", 8)}(?:${TOKEN_GAP}\\.${TOKEN_GAP}${tokenBody("[A-Za-z0-9_-]", 8)}){2}\\b`, "gu");
|
|
33
|
+
const PROVIDER_TOKEN_PATTERN = new RegExp(`\\b(?:${[
|
|
34
|
+
...["sk-", "glpat-", "npm_", "pypi-"].map((prefix) => providerToken(prefix, "[A-Za-z0-9_-]", 16)),
|
|
35
|
+
...["sk_live_", "sk_test_", "rk_live_", "rk_test_", "whsec_"].map((prefix) => providerToken(prefix, "[A-Za-z0-9]", 16)),
|
|
36
|
+
...["ghp_", "gho_", "ghu_", "ghs_", "ghr_", "github_pat_"].map((prefix) => providerToken(prefix, "[A-Za-z0-9_]", 20)),
|
|
37
|
+
...["xoxb-", "xoxa-", "xoxp-", "xoxr-", "xoxs-"].map((prefix) => providerToken(prefix, "[A-Za-z0-9-]", 10)),
|
|
38
|
+
providerToken("AIza", "[A-Za-z0-9_-]", 20),
|
|
39
|
+
providerToken("AKIA", "[0-9A-Z]", 16, 16),
|
|
40
|
+
].join("|")})\\b`, "gu");
|
|
41
|
+
export function redactSecrets(text) {
|
|
42
|
+
return text
|
|
43
|
+
.replace(/-----BEGIN (?:RSA |EC |OPENSSH |DSA |ENCRYPTED )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH |DSA |ENCRYPTED )?PRIVATE KEY-----/gu, "[REDACTED:PRIVATE_KEY]")
|
|
44
|
+
.replace(AUTHORIZATION_TOKEN_PATTERN, (_match, scheme) => `${scheme.replace(/[\p{Cc}\p{Cf}]/gu, "")} [REDACTED:AUTHORIZATION]`)
|
|
45
|
+
.replace(/\b((?:postgres(?:ql)?|mysql|mariadb|mongodb(?:\+srv)?|redis|amqps?):\/\/[^:\s/@]+:)[^@\s/]+@/giu, (_match, prefix) => `${prefix}[REDACTED:PASSWORD]@`)
|
|
46
|
+
.replace(JWT_TOKEN_PATTERN, "[REDACTED:JWT]")
|
|
47
|
+
.replace(PROVIDER_TOKEN_PATTERN, "[REDACTED:API_KEY]")
|
|
48
|
+
.replace(/(["']?)\b((?:(?:[A-Za-z][A-Za-z0-9]*[_-])*(?:api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|private[_-]?key|connection[_-]?(?:string|url)|database[_-]?url|token|secret|password|passwd|credential)|[A-Za-z][A-Za-z0-9]*(?:token|secret|password|passwd|credential|apiKey|privateKey|connectionString|databaseUrl)))\b\1(\s*[:=]\s*)(["']?)([^\s,"'};]{4,})\4/giu, (_match, keyQuote, name, separator, valueQuote, value) => `${keyQuote}${name}${keyQuote}${separator}${valueQuote}${redactedCredential(value)}${valueQuote}`)
|
|
49
|
+
.replace(/(["']?)\b(api[ _-]?key|access[ _-]?token|auth[ _-]?token|client[ _-]?secret|private[ _-]?key|verification[ _-]?token|password|passwd|secret|credential)\b\1(\s+(?:is|was|equals?)\s+)(["']?)([^\s,"'};]{4,})\4/giu, (_match, keyQuote, name, separator, valueQuote, value) => `${keyQuote}${name}${keyQuote}${separator}${valueQuote}${redactedCredential(value)}${valueQuote}`)
|
|
50
|
+
.replace(/\b(token|credential)\b(\s+(?:is|was|equals?)\s+)(["']?)([^\s,"'};]{8,})\3/giu, (match, name, separator, valueQuote, value) => {
|
|
51
|
+
const normalized = value
|
|
52
|
+
.replace(/^["']|["']$/gu, "")
|
|
53
|
+
.replace(/[.,!?]+$/gu, "");
|
|
54
|
+
const knownPlaceholder = NON_SECRET_TOKEN_VALUES.has(normalized.toLowerCase());
|
|
55
|
+
const classes = [
|
|
56
|
+
/[a-z]/u,
|
|
57
|
+
/[A-Z]/u,
|
|
58
|
+
/[0-9]/u,
|
|
59
|
+
/[^A-Za-z0-9]/u,
|
|
60
|
+
].filter((pattern) => pattern.test(normalized)).length;
|
|
61
|
+
return !knownPlaceholder &&
|
|
62
|
+
(normalized.length >= 16 ||
|
|
63
|
+
(normalized.length >= 10 && classes >= 2))
|
|
64
|
+
? `${name}${separator}${valueQuote}${redactedCredential(value)}${valueQuote}`
|
|
65
|
+
: match;
|
|
66
|
+
})
|
|
67
|
+
.replace(/\b((?:set-)?cookie\s*:\s*)[^\r\n]+/giu, (_match, prefix) => `${prefix}[REDACTED:AUTHORIZATION]`);
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=runtime-redaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-redaction.js","sourceRoot":"","sources":["../src/runtime-redaction.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { redactSecrets } from "./runtime-redaction.js";
|
|
1
2
|
export declare const COMMAND_HOOK_AGENT_NAMES: readonly ["codex", "claude", "copilot-cli", "copilot-vscode", "cursor", "polytoken"];
|
|
2
3
|
export type CommandHookAgentName = (typeof COMMAND_HOOK_AGENT_NAMES)[number];
|
|
3
4
|
/** @deprecated Use CommandHookAgentName for native command-hook integrations. */
|
|
@@ -17,6 +18,7 @@ export interface TurnRequest {
|
|
|
17
18
|
agent: CommandHookAgentName;
|
|
18
19
|
sessionId: string;
|
|
19
20
|
retainedReceiptIds?: string[];
|
|
21
|
+
task?: string;
|
|
20
22
|
previousAssistant: {
|
|
21
23
|
content: string;
|
|
22
24
|
id?: string;
|
|
@@ -115,7 +117,6 @@ export interface LineageMetadata {
|
|
|
115
117
|
* be attributed to the machine that produced them.
|
|
116
118
|
*/
|
|
117
119
|
export declare function readLineageMetadata(home?: string, environment?: NodeJS.ProcessEnv): Promise<LineageMetadata>;
|
|
118
|
-
export declare function redactSecrets(text: string): string;
|
|
119
120
|
export declare function createTurnRequest(input: HookInput, agent: CommandHookAgentName, pending: PendingAssistantMessage, now: Date, lineage?: LineageMetadata): Promise<TurnRequest | null>;
|
|
120
121
|
interface GuardTrigger {
|
|
121
122
|
action: "edit" | "commit" | "deploy" | "command";
|
|
@@ -133,5 +134,4 @@ export declare function handleHookEvent(value: unknown, agent: CommandHookAgentN
|
|
|
133
134
|
export declare function getCursorContext(sessionId: string, prompt: string, options?: HookRuntimeOptions): Promise<string>;
|
|
134
135
|
export declare function completeCursorResponse(sessionId: string, response: string, options?: HookRuntimeOptions): Promise<void>;
|
|
135
136
|
export declare function runHook(args?: readonly string[]): Promise<void>;
|
|
136
|
-
export {};
|
|
137
137
|
//# sourceMappingURL=runtime.d.ts.map
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/runtime.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import { redactSecrets } from "./runtime-redaction.js";
|
|
2
|
+
export { redactSecrets } from "./runtime-redaction.js";
|
|
1
3
|
import { createHash, randomUUID } from "node:crypto";
|
|
4
|
+
import { CAPTURE_CONFLICT_MESSAGES } from "@lore-co/core/capture-conflict";
|
|
2
5
|
import { approachGuidance } from "./approach-guidance.js";
|
|
6
|
+
import { skillGuidance } from "./skill-guidance.js";
|
|
3
7
|
import { retainedContextTexts, retainedReceiptIds, rememberDeliveredContext } from "./retained-context.js";
|
|
8
|
+
import { clearRetrievalTask, resolveRetrievalTask } from "./retrieval-task.js";
|
|
4
9
|
import { access, chmod, mkdir, readFile, stat, } from "node:fs/promises";
|
|
5
10
|
import { constants as fsConstants, realpathSync } from "node:fs";
|
|
6
11
|
import { execFile as execFileCallback } from "node:child_process";
|
|
@@ -8,7 +13,7 @@ import { dirname, parse, relative, resolve } from "node:path";
|
|
|
8
13
|
import { homedir } from "node:os";
|
|
9
14
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
10
15
|
import { promisify } from "node:util";
|
|
11
|
-
import { boundedUtf8Text, repositoryScopeFromGitRoot, } from "./repository.js";
|
|
16
|
+
import { boundedUtf8Text, defaultGitPushBranches, repositoryScopeFromGitRoot, } from "./repository.js";
|
|
12
17
|
import { ReliabilityStore, ReliabilityStoreError, atomicWriteJson as durableAtomicWriteJson, classifyRetryFailure, createIntegrationInvocationAttempt, durableUnlink, recordLocalGuardMetric, recordLocalRetrievalMetric, } from "./reliability-store.js";
|
|
13
18
|
import { writeInvocationAttemptBounded, writeInvocationCompletionBounded, } from "./invocation-health-writer.js";
|
|
14
19
|
import { SignedSnapshotError, verifySignedSnapshot, } from "./signed-snapshot.js";
|
|
@@ -58,24 +63,6 @@ const RELIABILITY_REASON_CODES = new Set([
|
|
|
58
63
|
"invalid_response",
|
|
59
64
|
"integration_unsupported",
|
|
60
65
|
]);
|
|
61
|
-
const NON_SECRET_TOKEN_VALUES = new Set([
|
|
62
|
-
"available",
|
|
63
|
-
"configured",
|
|
64
|
-
"generated",
|
|
65
|
-
"optional",
|
|
66
|
-
"placeholder",
|
|
67
|
-
"redacted",
|
|
68
|
-
"required",
|
|
69
|
-
"undefined",
|
|
70
|
-
]);
|
|
71
|
-
function redactedCredential(value) {
|
|
72
|
-
// Preserve markers from earlier token rules and repeated redaction passes.
|
|
73
|
-
if (/^\[REDACTED(?::[A-Z_]+|_[A-Z_]+)?\][.,!?]*$/u.test(value)) {
|
|
74
|
-
return value;
|
|
75
|
-
}
|
|
76
|
-
const trailing = /[.,!?]+$/u.exec(value)?.[0] ?? "";
|
|
77
|
-
return `[REDACTED:CREDENTIAL]${trailing}`;
|
|
78
|
-
}
|
|
79
66
|
const execFile = promisify(execFileCallback);
|
|
80
67
|
function homeDirectory(override) {
|
|
81
68
|
return override ?? process.env.LORE_HOME ?? process.env.HOME ?? homedir();
|
|
@@ -339,52 +326,6 @@ function deterministicUuid(value) {
|
|
|
339
326
|
joined.slice(20, 32),
|
|
340
327
|
].join("-");
|
|
341
328
|
}
|
|
342
|
-
// This standalone runtime cannot import @lore-co/core after it is copied to
|
|
343
|
-
// ~/.lore. Keep these rules aligned with core/redaction.ts through parity tests.
|
|
344
|
-
// Match invisible characters inside tokens without changing ordinary text;
|
|
345
|
-
// real whitespace still separates token bodies and adjacent lines.
|
|
346
|
-
const TOKEN_GAP = "(?:\\p{Cf}|(?!\\s)\\p{Cc})*";
|
|
347
|
-
const tokenPrefix = (prefix) => Array.from(prefix, (character) => character.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")).join(TOKEN_GAP);
|
|
348
|
-
const tokenBody = (characters, minimum, maximum) => `${characters}(?:${TOKEN_GAP}${characters}){${minimum - 1},${maximum === undefined ? "" : maximum - 1}}`;
|
|
349
|
-
const providerToken = (prefix, characters, minimum, maximum) => tokenPrefix(prefix) + TOKEN_GAP + tokenBody(characters, minimum, maximum);
|
|
350
|
-
const AUTHORIZATION_TOKEN_PATTERN = new RegExp(`\\b(${tokenPrefix("Bearer")}|${tokenPrefix("Basic")})[\\s\\p{Cc}\\p{Cf}]+${tokenBody("[A-Za-z0-9._~+/=-]", 8)}`, "giu");
|
|
351
|
-
const JWT_TOKEN_PATTERN = new RegExp(`\\b${tokenPrefix("eyJ")}${TOKEN_GAP}${tokenBody("[A-Za-z0-9_-]", 8)}(?:${TOKEN_GAP}\\.${TOKEN_GAP}${tokenBody("[A-Za-z0-9_-]", 8)}){2}\\b`, "gu");
|
|
352
|
-
const PROVIDER_TOKEN_PATTERN = new RegExp(`\\b(?:${[
|
|
353
|
-
...["sk-", "glpat-", "npm_", "pypi-"].map((prefix) => providerToken(prefix, "[A-Za-z0-9_-]", 16)),
|
|
354
|
-
...["sk_live_", "sk_test_", "rk_live_", "rk_test_", "whsec_"].map((prefix) => providerToken(prefix, "[A-Za-z0-9]", 16)),
|
|
355
|
-
...["ghp_", "gho_", "ghu_", "ghs_", "ghr_", "github_pat_"].map((prefix) => providerToken(prefix, "[A-Za-z0-9_]", 20)),
|
|
356
|
-
...["xoxb-", "xoxa-", "xoxp-", "xoxr-", "xoxs-"].map((prefix) => providerToken(prefix, "[A-Za-z0-9-]", 10)),
|
|
357
|
-
providerToken("AIza", "[A-Za-z0-9_-]", 20),
|
|
358
|
-
providerToken("AKIA", "[0-9A-Z]", 16, 16),
|
|
359
|
-
].join("|")})\\b`, "gu");
|
|
360
|
-
export function redactSecrets(text) {
|
|
361
|
-
return text
|
|
362
|
-
.replace(/-----BEGIN (?:RSA |EC |OPENSSH |DSA |ENCRYPTED )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH |DSA |ENCRYPTED )?PRIVATE KEY-----/gu, "[REDACTED:PRIVATE_KEY]")
|
|
363
|
-
.replace(AUTHORIZATION_TOKEN_PATTERN, (_match, scheme) => `${scheme.replace(/[\p{Cc}\p{Cf}]/gu, "")} [REDACTED:AUTHORIZATION]`)
|
|
364
|
-
.replace(/\b((?:postgres(?:ql)?|mysql|mariadb|mongodb(?:\+srv)?|redis|amqps?):\/\/[^:\s/@]+:)[^@\s/]+@/giu, (_match, prefix) => `${prefix}[REDACTED:PASSWORD]@`)
|
|
365
|
-
.replace(JWT_TOKEN_PATTERN, "[REDACTED:JWT]")
|
|
366
|
-
.replace(PROVIDER_TOKEN_PATTERN, "[REDACTED:API_KEY]")
|
|
367
|
-
.replace(/(["']?)\b((?:(?:[A-Za-z][A-Za-z0-9]*[_-])*(?:api[_-]?key|access[_-]?token|auth[_-]?token|client[_-]?secret|private[_-]?key|connection[_-]?(?:string|url)|database[_-]?url|token|secret|password|passwd|credential)|[A-Za-z][A-Za-z0-9]*(?:token|secret|password|passwd|credential|apiKey|privateKey|connectionString|databaseUrl)))\b\1(\s*[:=]\s*)(["']?)([^\s,"'};]{4,})\4/giu, (_match, keyQuote, name, separator, valueQuote, value) => `${keyQuote}${name}${keyQuote}${separator}${valueQuote}${redactedCredential(value)}${valueQuote}`)
|
|
368
|
-
.replace(/(["']?)\b(api[ _-]?key|access[ _-]?token|auth[ _-]?token|client[ _-]?secret|private[ _-]?key|verification[ _-]?token|password|passwd|secret|credential)\b\1(\s+(?:is|was|equals?)\s+)(["']?)([^\s,"'};]{4,})\4/giu, (_match, keyQuote, name, separator, valueQuote, value) => `${keyQuote}${name}${keyQuote}${separator}${valueQuote}${redactedCredential(value)}${valueQuote}`)
|
|
369
|
-
.replace(/\b(token|credential)\b(\s+(?:is|was|equals?)\s+)(["']?)([^\s,"'};]{8,})\3/giu, (match, name, separator, valueQuote, value) => {
|
|
370
|
-
const normalized = value
|
|
371
|
-
.replace(/^["']|["']$/gu, "")
|
|
372
|
-
.replace(/[.,!?]+$/gu, "");
|
|
373
|
-
const knownPlaceholder = NON_SECRET_TOKEN_VALUES.has(normalized.toLowerCase());
|
|
374
|
-
const classes = [
|
|
375
|
-
/[a-z]/u,
|
|
376
|
-
/[A-Z]/u,
|
|
377
|
-
/[0-9]/u,
|
|
378
|
-
/[^A-Za-z0-9]/u,
|
|
379
|
-
].filter((pattern) => pattern.test(normalized)).length;
|
|
380
|
-
return !knownPlaceholder &&
|
|
381
|
-
(normalized.length >= 16 ||
|
|
382
|
-
(normalized.length >= 10 && classes >= 2))
|
|
383
|
-
? `${name}${separator}${valueQuote}${redactedCredential(value)}${valueQuote}`
|
|
384
|
-
: match;
|
|
385
|
-
})
|
|
386
|
-
.replace(/\b((?:set-)?cookie\s*:\s*)[^\r\n]+/giu, (_match, prefix) => `${prefix}[REDACTED:AUTHORIZATION]`);
|
|
387
|
-
}
|
|
388
329
|
async function atomicWriteJson(path, value) {
|
|
389
330
|
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
390
331
|
await chmod(dirname(path), 0o700);
|
|
@@ -703,17 +644,38 @@ function contextDeliveriesUrl(apiUrl) {
|
|
|
703
644
|
}
|
|
704
645
|
class RuntimeHttpError extends Error {
|
|
705
646
|
httpStatus;
|
|
706
|
-
|
|
647
|
+
retryable;
|
|
648
|
+
constructor(message, httpStatus, retryable) {
|
|
707
649
|
super(message);
|
|
708
650
|
this.name = "RuntimeHttpError";
|
|
709
651
|
this.httpStatus = httpStatus;
|
|
710
|
-
|
|
711
|
-
}
|
|
712
|
-
|
|
652
|
+
this.retryable = retryable;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
async function captureHttpError(response, operation) {
|
|
656
|
+
// Keep only known diagnostic fields: an upstream error body may contain
|
|
657
|
+
// captured text, secrets, or HTML. A request ID lets support find server logs.
|
|
658
|
+
const body = await boundedResponseJson(response, 8_192).catch(() => null);
|
|
659
|
+
const reason = isObject(body) && typeof body.reason === "string" &&
|
|
660
|
+
Object.hasOwn(CAPTURE_CONFLICT_MESSAGES, body.reason) ? body.reason : undefined;
|
|
661
|
+
const legacyMessages = new Set([
|
|
662
|
+
"Connector event ID was reused with a different observation",
|
|
663
|
+
"Connector event ID was reused with a different request",
|
|
664
|
+
"Connector event ID is already used by another event type",
|
|
665
|
+
]);
|
|
666
|
+
const detail = reason === undefined
|
|
667
|
+
? isObject(body) && typeof body.message === "string" && legacyMessages.has(body.message) ? body.message : undefined
|
|
668
|
+
: `${reason}: ${CAPTURE_CONFLICT_MESSAGES[reason]}`;
|
|
669
|
+
const requestId = response.headers.get("x-request-id") ?? (isObject(body) ? body.requestId : undefined);
|
|
670
|
+
const reference = typeof requestId === "string" && /^[a-zA-Z0-9._:-]{1,128}$/u.test(requestId)
|
|
671
|
+
? ` (request ${requestId})` : "";
|
|
672
|
+
return new RuntimeHttpError(`${operation} failed with HTTP ${response.status}${detail ? `: ${detail}` : ""}${reference}`, response.status, response.status === 409 && isObject(body) && body.retryable === true ? true : undefined);
|
|
673
|
+
}
|
|
674
|
+
async function boundedResponseJson(response, maxBytes = MAX_RESPONSE_BYTES) {
|
|
713
675
|
const declaredLength = response.headers.get("content-length");
|
|
714
676
|
if (declaredLength !== null &&
|
|
715
677
|
Number.isFinite(Number(declaredLength)) &&
|
|
716
|
-
Number(declaredLength) >
|
|
678
|
+
Number(declaredLength) > maxBytes) {
|
|
717
679
|
await response.body?.cancel();
|
|
718
680
|
throw new Error("Lore response exceeds the reliability size limit");
|
|
719
681
|
}
|
|
@@ -730,7 +692,7 @@ async function boundedResponseJson(response) {
|
|
|
730
692
|
break;
|
|
731
693
|
}
|
|
732
694
|
bytes += chunk.value.byteLength;
|
|
733
|
-
if (bytes >
|
|
695
|
+
if (bytes > maxBytes) {
|
|
734
696
|
await reader.cancel();
|
|
735
697
|
throw new Error("Lore response exceeds the reliability size limit");
|
|
736
698
|
}
|
|
@@ -839,6 +801,10 @@ function deliveryFromResponse(value) {
|
|
|
839
801
|
: memorySummaries(value.memories);
|
|
840
802
|
return {
|
|
841
803
|
context: boundedContext(context),
|
|
804
|
+
...(typeof value.relatedContext === "string" ? { relatedContext: redactSecrets(value.relatedContext).slice(0, 2_500) }
|
|
805
|
+
: isObject(value.context) && typeof value.context.relatedContext === "string" ? { relatedContext: redactSecrets(value.context.relatedContext).slice(0, 2_500) } : {}),
|
|
806
|
+
...(typeof value.skillContext === "string" && value.skillContext.length <= 72_000 ? { skillContext: redactSecrets(value.skillContext) }
|
|
807
|
+
: isObject(value.context) && typeof value.context.skillContext === "string" && value.context.skillContext.length <= 72_000 ? { skillContext: redactSecrets(value.context.skillContext) } : {}),
|
|
842
808
|
completeContext: context.length <= MAX_CONTEXT_CHARS,
|
|
843
809
|
...(receiptId === undefined ? {} : { receiptId }),
|
|
844
810
|
learned,
|
|
@@ -891,6 +857,8 @@ function deliveryFromSignedContext(payload, expectedRequest) {
|
|
|
891
857
|
}
|
|
892
858
|
return deliveryFromResponse({
|
|
893
859
|
context: payload.context,
|
|
860
|
+
relatedContext: payload.relatedContext,
|
|
861
|
+
skillContext: payload.skillContext,
|
|
894
862
|
receipt: { id: payload.receiptId },
|
|
895
863
|
memories: payload.memories,
|
|
896
864
|
hits: payload.hits,
|
|
@@ -1137,6 +1105,8 @@ async function postTurn(config, request, fetchImplementation, store) {
|
|
|
1137
1105
|
const response = await fetchImplementation(turnsUrl(config.apiUrl), {
|
|
1138
1106
|
method: "POST",
|
|
1139
1107
|
headers: {
|
|
1108
|
+
"x-lore-context": "related-work-v1",
|
|
1109
|
+
"x-lore-skills": "skills-v1",
|
|
1140
1110
|
accept: runtimeAccept(config),
|
|
1141
1111
|
authorization: `Bearer ${config.token}`,
|
|
1142
1112
|
"content-type": "application/json",
|
|
@@ -1152,7 +1122,7 @@ async function postTurn(config, request, fetchImplementation, store) {
|
|
|
1152
1122
|
return postTurn(config, compatible, fetchImplementation, store);
|
|
1153
1123
|
}
|
|
1154
1124
|
if (!response.ok) {
|
|
1155
|
-
throw
|
|
1125
|
+
throw await captureHttpError(response, "Lore turn request");
|
|
1156
1126
|
}
|
|
1157
1127
|
const value = await protocolResponseJson(response);
|
|
1158
1128
|
if (config.version === 1) {
|
|
@@ -1202,7 +1172,7 @@ async function postTurn(config, request, fetchImplementation, store) {
|
|
|
1202
1172
|
task: {
|
|
1203
1173
|
agent: request.agent,
|
|
1204
1174
|
...(request.scope === undefined ? {} : { scope: request.scope }),
|
|
1205
|
-
task: request.currentUser.content.trim(),
|
|
1175
|
+
task: request.task ?? request.currentUser.content.trim(),
|
|
1206
1176
|
},
|
|
1207
1177
|
});
|
|
1208
1178
|
await store
|
|
@@ -1248,7 +1218,7 @@ async function postPromptObservation(config, request, fetchImplementation, _stor
|
|
|
1248
1218
|
signal: AbortSignal.timeout(config.timeoutMs ?? 2_500),
|
|
1249
1219
|
});
|
|
1250
1220
|
if (!response.ok) {
|
|
1251
|
-
throw
|
|
1221
|
+
throw await captureHttpError(response, "Lore prompt observation");
|
|
1252
1222
|
}
|
|
1253
1223
|
const value = await protocolResponseJson(response);
|
|
1254
1224
|
if (config.version === 1) {
|
|
@@ -1284,6 +1254,8 @@ async function getPromptContext(config, agent, sessionId, sourceEventId, prompt,
|
|
|
1284
1254
|
const response = await fetchImplementation(contextDeliveriesUrl(config.apiUrl), {
|
|
1285
1255
|
method: "POST",
|
|
1286
1256
|
headers: {
|
|
1257
|
+
"x-lore-context": "related-work-v1",
|
|
1258
|
+
"x-lore-skills": "skills-v1",
|
|
1287
1259
|
accept: runtimeAccept(config),
|
|
1288
1260
|
authorization: `Bearer ${config.token}`,
|
|
1289
1261
|
"content-type": "application/json",
|
|
@@ -1414,6 +1386,7 @@ async function getCachedPromptContext(config, prompt, gitContext, store, now, re
|
|
|
1414
1386
|
}
|
|
1415
1387
|
return {
|
|
1416
1388
|
context: retained.includes(String(selected.payload.receiptId)) ? "" : selected.context,
|
|
1389
|
+
skillContext: "Lore is using saved learning context. Enabled skills could not be checked and saved skill instructions have not been reapplied. Reconnect to Lore before relying on skills for this task.",
|
|
1417
1390
|
learned: [],
|
|
1418
1391
|
delivered: [],
|
|
1419
1392
|
reliability: {
|
|
@@ -1501,6 +1474,35 @@ async function retrievalNotice(delivery, sessionId, store, now) {
|
|
|
1501
1474
|
function queueDirectory(home) {
|
|
1502
1475
|
return resolve(loreDirectory(home), "queue");
|
|
1503
1476
|
}
|
|
1477
|
+
async function stableCapture(store, queued) {
|
|
1478
|
+
// Hook retries reconstruct timestamps, diffs, and lineage. Freeze the first
|
|
1479
|
+
// request so the same source event always replays identical capture bytes,
|
|
1480
|
+
// including after its outbox acknowledgement or a process restart. Retain
|
|
1481
|
+
// just the latest request per session/kind, not an unbounded event journal.
|
|
1482
|
+
const sourceEventId = queued.request.eventId;
|
|
1483
|
+
const key = `native-capture:${queued.kind}:${queued.request.agent}:${sha256(queued.request.sessionId)}`;
|
|
1484
|
+
const pending = (await store.listOutbox()).find(entry => entry.idempotencyKey === sourceEventId);
|
|
1485
|
+
const original = pending ? queuedRequest(pending.payload) : null;
|
|
1486
|
+
const state = await store.updateState(key, current => {
|
|
1487
|
+
const previous = isObject(current) && current.sourceEventId === sourceEventId ? queuedRequest(current.queued) : original;
|
|
1488
|
+
if (previous && previous.kind === queued.kind) {
|
|
1489
|
+
if (previous.request.idempotencyKey !== queued.request.idempotencyKey) {
|
|
1490
|
+
throw new ReliabilityStoreError("IDEMPOTENCY_CONFLICT", "A captured source event changed; its original recording was retained");
|
|
1491
|
+
}
|
|
1492
|
+
return { sourceEventId, queued: previous };
|
|
1493
|
+
}
|
|
1494
|
+
// Hosts without prompt IDs can legitimately say "continue" again after a
|
|
1495
|
+
// different prompt. Their content hash is only useful for consecutive retry
|
|
1496
|
+
// detection; it must not be a session-wide event identity.
|
|
1497
|
+
const request = queued.kind === "prompt" && queued.request.promptId === undefined
|
|
1498
|
+
? { ...queued.request, eventId: randomUUID() } : queued.request;
|
|
1499
|
+
return { sourceEventId, queued: { ...queued, request } };
|
|
1500
|
+
});
|
|
1501
|
+
const saved = isObject(state) ? queuedRequest(state.queued) : null;
|
|
1502
|
+
if (saved === null || saved.kind !== queued.kind)
|
|
1503
|
+
throw new Error("Saved capture request is invalid");
|
|
1504
|
+
return saved;
|
|
1505
|
+
}
|
|
1504
1506
|
async function enqueueCapture(store, queued) {
|
|
1505
1507
|
return (await store.enqueue({
|
|
1506
1508
|
kind: queued.kind,
|
|
@@ -1904,6 +1906,7 @@ async function offlineGuard(config, store, request, now, previouslyEnforced, all
|
|
|
1904
1906
|
...(typeof request.repo === "string" ? { repo: request.repo } : {}),
|
|
1905
1907
|
...(typeof request.path === "string" ? { path: request.path } : {}),
|
|
1906
1908
|
...(typeof request.command === "string" ? { command: request.command } : {}),
|
|
1909
|
+
...(Array.isArray(request.defaultPushBranches) ? { defaultPushBranches: request.defaultPushBranches.filter((branch) => typeof branch === "string") } : {}),
|
|
1907
1910
|
...(Array.isArray(request.files) ? { files: request.files.filter((file) => typeof file === "string") } : {}),
|
|
1908
1911
|
}) !== null) : [];
|
|
1909
1912
|
return {
|
|
@@ -1976,6 +1979,12 @@ async function handleGuardEvent(input, agent, config, sessionId, options, now) {
|
|
|
1976
1979
|
const startedAt = Date.now();
|
|
1977
1980
|
let result;
|
|
1978
1981
|
const guardRequest = {
|
|
1982
|
+
...await (async () => {
|
|
1983
|
+
const args = isObject(input.tool_input) ? input.tool_input : {};
|
|
1984
|
+
const changesContext = ["cwd", "workdir", "working_directory", "env", "environment"].some(key => args[key] !== undefined);
|
|
1985
|
+
const defaultPushBranches = changesContext ? undefined : await defaultGitPushBranches(trigger.command, cwd);
|
|
1986
|
+
return defaultPushBranches ? { defaultPushBranches } : {};
|
|
1987
|
+
})(),
|
|
1979
1988
|
connector: "lore-cli",
|
|
1980
1989
|
agent,
|
|
1981
1990
|
sessionId,
|
|
@@ -2103,6 +2112,9 @@ export async function handleHookEvent(value, agent, options = {}) {
|
|
|
2103
2112
|
const access = await checkLoreAccess(stringField(input.cwd), options.home, config, options.fetch ?? globalThis.fetch);
|
|
2104
2113
|
if (!access.allowed) {
|
|
2105
2114
|
await clearPending(agent, sessionId, options.home, config.connectionId);
|
|
2115
|
+
await runtimeStore(config, options.home).then(store => clearRetrievalTask(store, {
|
|
2116
|
+
agent, sessionId, connectionKey: credentialReliabilityWorkspaceKey(config),
|
|
2117
|
+
})).catch(() => undefined);
|
|
2106
2118
|
// A policy outage must not silently bypass an already enforced Guard.
|
|
2107
2119
|
if (eventName === "PreToolUse" && access.reason === "Workspace repository settings are unavailable") {
|
|
2108
2120
|
const prior = await readGuardState(agent, sessionId, options.home, config.connectionId).catch(() => null);
|
|
@@ -2239,8 +2251,10 @@ async function cursorSession(sessionId, options) {
|
|
|
2239
2251
|
throw new Error("Start a new Cursor conversation to register this Lore session.");
|
|
2240
2252
|
}
|
|
2241
2253
|
const access = await checkLoreAccess(stringField(state.cwd), options.home, config, options.fetch ?? globalThis.fetch);
|
|
2242
|
-
if (!access.allowed)
|
|
2254
|
+
if (!access.allowed) {
|
|
2255
|
+
await clearRetrievalTask(store, { agent: "cursor", sessionId, connectionKey: credentialReliabilityWorkspaceKey(config) }).catch(() => undefined);
|
|
2243
2256
|
throw new Error(`Lore is off: ${access.reason}.`);
|
|
2257
|
+
}
|
|
2244
2258
|
return { config, store, state, cwd: stringField(state.cwd) };
|
|
2245
2259
|
}
|
|
2246
2260
|
export async function getCursorContext(sessionId, prompt, options = {}) {
|
|
@@ -2292,11 +2306,17 @@ async function handlePromptEvent(input, agent, sessionId, config, options, now,
|
|
|
2292
2306
|
notices.push("Lore retained a capture locally, but upload is blocked by authentication. Run lore connect again.");
|
|
2293
2307
|
}
|
|
2294
2308
|
else if (result?.state === "dead") {
|
|
2295
|
-
notices.push("Lore retained a
|
|
2309
|
+
notices.push("Lore retained a rejected or incompatible capture locally for diagnosis. Run lore doctor.");
|
|
2296
2310
|
}
|
|
2297
2311
|
};
|
|
2312
|
+
const terminal = (result, eventId) => result?.idempotencyKey === eventId && (result.state === "dead" || result.state === "auth-blocked");
|
|
2298
2313
|
try {
|
|
2299
|
-
|
|
2314
|
+
const flushed = await flushOne(config, fetchImplementation, store);
|
|
2315
|
+
noticeForFlush(flushed);
|
|
2316
|
+
// A retained terminal failure still occupies capacity. Give the next item
|
|
2317
|
+
// one upload attempt before enqueueing, including when the queue was full.
|
|
2318
|
+
if (flushed?.state === "dead")
|
|
2319
|
+
noticeForFlush(await flushOne(config, fetchImplementation, store));
|
|
2300
2320
|
}
|
|
2301
2321
|
catch {
|
|
2302
2322
|
notices.push("Lore retained queued captures locally, but replay could not run. Run lore doctor.");
|
|
@@ -2327,18 +2347,30 @@ async function handlePromptEvent(input, agent, sessionId, config, options, now,
|
|
|
2327
2347
|
};
|
|
2328
2348
|
const lineage = await readLineageMetadata(options.home, options.environment ?? process.env);
|
|
2329
2349
|
let delivery = emptyDelivery();
|
|
2350
|
+
const retrievalTask = (repo) => resolveRetrievalTask(store, {
|
|
2351
|
+
agent, sessionId, connectionKey: credentialReliabilityWorkspaceKey(config),
|
|
2352
|
+
cwd: stringField(input.cwd), repo,
|
|
2353
|
+
}, prompt, now, pending?.cwd === stringField(input.cwd) && pending
|
|
2354
|
+
? { content: pending.assistantMessage, capturedAt: pending.capturedAt } : undefined);
|
|
2330
2355
|
if (pending !== null) {
|
|
2331
2356
|
let receivedTurnContext = false;
|
|
2332
|
-
|
|
2357
|
+
let request = await createTurnRequest(input, agent, pending, now, lineage);
|
|
2333
2358
|
if (request === null) {
|
|
2334
2359
|
return undefined;
|
|
2335
2360
|
}
|
|
2361
|
+
const task = await retrievalTask(request.scope?.repo);
|
|
2362
|
+
if (task !== request.currentUser.content.trim())
|
|
2363
|
+
request.task = task;
|
|
2336
2364
|
if (retained.length)
|
|
2337
2365
|
request.retainedReceiptIds = retained;
|
|
2338
2366
|
let enqueued = false;
|
|
2339
2367
|
try {
|
|
2340
|
-
await
|
|
2341
|
-
|
|
2368
|
+
request = (await stableCapture(store, { kind: "turn", request })).request;
|
|
2369
|
+
const saved = await enqueueCapture(store, { kind: "turn", request });
|
|
2370
|
+
enqueued = saved.state === "ready" || saved.state === "in-flight";
|
|
2371
|
+
if (saved.state === "dead" || saved.state === "auth-blocked") {
|
|
2372
|
+
noticeForFlush({ idempotencyKey: saved.idempotencyKey, state: saved.state });
|
|
2373
|
+
}
|
|
2342
2374
|
await rememberImages(request.eventId).catch(() => undefined);
|
|
2343
2375
|
await clearPending(agent, sessionId, options.home, config.connectionId);
|
|
2344
2376
|
}
|
|
@@ -2360,7 +2392,7 @@ async function handlePromptEvent(input, agent, sessionId, config, options, now,
|
|
|
2360
2392
|
receivedTurnContext = delivery.reliability?.status !== "failed" &&
|
|
2361
2393
|
delivery.reliability?.source !== "none";
|
|
2362
2394
|
}
|
|
2363
|
-
else {
|
|
2395
|
+
else if (!terminal(flushed, request.eventId)) {
|
|
2364
2396
|
notices.push("Lore saved this capture locally; server upload is pending.");
|
|
2365
2397
|
}
|
|
2366
2398
|
}
|
|
@@ -2370,7 +2402,7 @@ async function handlePromptEvent(input, agent, sessionId, config, options, now,
|
|
|
2370
2402
|
}
|
|
2371
2403
|
if (retrieve && !receivedTurnContext) {
|
|
2372
2404
|
try {
|
|
2373
|
-
delivery = await getPromptContextWithFallback(config, agent, sessionId, request.eventId,
|
|
2405
|
+
delivery = await getPromptContextWithFallback(config, agent, sessionId, request.eventId, task, {
|
|
2374
2406
|
...(request.scope === undefined ? {} : { scope: request.scope }),
|
|
2375
2407
|
learningScope: request.learningScope,
|
|
2376
2408
|
...(request.diff === undefined ? {} : { diff: request.diff }),
|
|
@@ -2399,15 +2431,20 @@ async function handlePromptEvent(input, agent, sessionId, config, options, now,
|
|
|
2399
2431
|
}
|
|
2400
2432
|
else {
|
|
2401
2433
|
const gitContext = await collectGitContext(stringField(input.cwd));
|
|
2402
|
-
const
|
|
2434
|
+
const task = await retrievalTask(gitContext.scope?.repo);
|
|
2435
|
+
let observation = createPromptObservationRequest(input, agent, sessionId, now, gitContext, lineage);
|
|
2403
2436
|
if (observation === null) {
|
|
2404
2437
|
return undefined;
|
|
2405
2438
|
}
|
|
2406
2439
|
let enqueued = false;
|
|
2407
2440
|
try {
|
|
2408
2441
|
if (capture) {
|
|
2409
|
-
await
|
|
2410
|
-
|
|
2442
|
+
observation = (await stableCapture(store, { kind: "prompt", request: observation })).request;
|
|
2443
|
+
const saved = await enqueueCapture(store, { kind: "prompt", request: observation });
|
|
2444
|
+
enqueued = saved.state === "ready" || saved.state === "in-flight";
|
|
2445
|
+
if (saved.state === "dead" || saved.state === "auth-blocked") {
|
|
2446
|
+
noticeForFlush({ idempotencyKey: saved.idempotencyKey, state: saved.state });
|
|
2447
|
+
}
|
|
2411
2448
|
await rememberImages(observation.eventId).catch(() => undefined);
|
|
2412
2449
|
}
|
|
2413
2450
|
}
|
|
@@ -2421,8 +2458,8 @@ async function handlePromptEvent(input, agent, sessionId, config, options, now,
|
|
|
2421
2458
|
try {
|
|
2422
2459
|
const flushed = await flushOne(config, fetchImplementation, store);
|
|
2423
2460
|
noticeForFlush(flushed);
|
|
2424
|
-
if (flushed
|
|
2425
|
-
flushed.state !== "acknowledged") {
|
|
2461
|
+
if (!terminal(flushed, observation.eventId) &&
|
|
2462
|
+
(flushed?.idempotencyKey !== observation.eventId || flushed.state !== "acknowledged")) {
|
|
2426
2463
|
notices.push("Lore saved this capture locally; server upload is pending.");
|
|
2427
2464
|
}
|
|
2428
2465
|
}
|
|
@@ -2432,7 +2469,7 @@ async function handlePromptEvent(input, agent, sessionId, config, options, now,
|
|
|
2432
2469
|
}
|
|
2433
2470
|
if (retrieve) {
|
|
2434
2471
|
try {
|
|
2435
|
-
delivery = await getPromptContextWithFallback(config, agent, sessionId, observation.eventId,
|
|
2472
|
+
delivery = await getPromptContextWithFallback(config, agent, sessionId, observation.eventId, task, gitContext, fetchImplementation, store, now, retained);
|
|
2436
2473
|
}
|
|
2437
2474
|
catch {
|
|
2438
2475
|
delivery = {
|
|
@@ -2480,7 +2517,10 @@ async function handlePromptEvent(input, agent, sessionId, config, options, now,
|
|
|
2480
2517
|
const receipt = receiptMessage(config, agent, delivery);
|
|
2481
2518
|
if (retrieve && delivery.completeContext)
|
|
2482
2519
|
await rememberDeliveredContext(store, contextSession, delivery).catch(() => undefined);
|
|
2483
|
-
const
|
|
2520
|
+
const relatedContext = delivery.relatedContext && !retainedContext.some(text => text.includes(delivery.relatedContext))
|
|
2521
|
+
? delivery.relatedContext : undefined;
|
|
2522
|
+
// Skills are fresh on every turn, even when learning context is retained.
|
|
2523
|
+
const taskContext = [delivery.skillContext, delivery.context, relatedContext, delivery.reliability?.status === "failed" ? undefined : approachGuidance(prompt, agent, redactSecrets(sessionId), retainedContext), delivery.skillContext === undefined && delivery.reliability?.status !== "failed" ? skillGuidance(retainedContext) : undefined].filter(Boolean).join("\n\n");
|
|
2484
2524
|
const systemMessage = agent === "cursor" || agent === "polytoken"
|
|
2485
2525
|
? undefined
|
|
2486
2526
|
: [receipt, reliabilityNotice].filter(Boolean).join(" · ") || undefined;
|