@yanlinglabs/winter-agent-sdk 0.0.2 → 0.0.3
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 +30 -0
- package/dist/{index-h1tryj38.js → index-51ysrfm8.js} +1 -1
- package/dist/index-9e98bg1r.js +707 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +64 -744
- package/dist/messaging/index.js +1 -1
- package/dist/messaging/router.d.ts +10 -0
- package/dist/paths/project-key.d.ts +2 -0
- package/dist/tools/accept.d.ts +39 -0
- package/dist/tools/advisor.d.ts +106 -0
- package/dist/tools/definitions.d.ts +31 -0
- package/dist/tools/index.d.ts +14 -0
- package/dist/tools/index.js +438 -0
- package/dist/tools/messaging-handlers.d.ts +52 -0
- package/dist/tools/port.d.ts +39 -0
- package/dist/tools/schemas.d.ts +48 -0
- package/dist/version.d.ts +1 -0
- package/package.json +9 -5
package/dist/index.js
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BRAND_TOKEN_RE2,
|
|
3
|
+
FIRST_PARTY_ORIGINATORS2,
|
|
4
|
+
WINTER_BRAND2,
|
|
5
|
+
resolveBrand2,
|
|
6
|
+
envName2,
|
|
7
|
+
mcpToolName2,
|
|
8
|
+
userAgent2,
|
|
9
|
+
isUnset2,
|
|
10
|
+
resolveWinterHome2,
|
|
11
|
+
resolveKeychainServiceForProfile2,
|
|
12
|
+
WinterStoreError2,
|
|
13
|
+
WinterStoreLeaseError2,
|
|
14
|
+
DIALECT_RECORD_ENTRY_TYPE2,
|
|
15
|
+
PROVIDER_STATE_FILE_SUFFIX2,
|
|
16
|
+
WinterCompatibilitySessionStore2
|
|
17
|
+
} from "./index-9e98bg1r.js";
|
|
1
18
|
import {
|
|
2
19
|
deliveryUncertain2,
|
|
3
20
|
refused2,
|
|
4
21
|
unavailable2
|
|
5
|
-
} from "./index-
|
|
22
|
+
} from "./index-51ysrfm8.js";
|
|
6
23
|
|
|
7
24
|
// src/query.ts
|
|
8
25
|
import { randomUUID } from "node:crypto";
|
|
@@ -41,120 +58,14 @@ function splitFrames(chunk, carry) {
|
|
|
41
58
|
return { frames, carry: nextCarry };
|
|
42
59
|
}
|
|
43
60
|
|
|
44
|
-
// src/brand.ts
|
|
45
|
-
var BRAND_TOKEN_RE = /^[a-z][a-z0-9-]{0,31}$/;
|
|
46
|
-
var DOT_DIR_RE = /^\.[a-z][a-z0-9-]{0,31}$/;
|
|
47
|
-
var INSTRUCTIONS_FILE_RE = /^[A-Z][A-Z0-9_]{0,31}\.md$/;
|
|
48
|
-
var ENV_PREFIX_RE = /^[A-Z][A-Z0-9]{0,15}_$/;
|
|
49
|
-
var KEYCHAIN_SERVICE_RE = /^[a-z][a-z0-9.-]{0,63}$/;
|
|
50
|
-
var MAX_PRODUCT_NAME = 64;
|
|
51
|
-
var CONTROL_BYTE_RE = /[\u0000-\u001f\u007f]/;
|
|
52
|
-
function isHttpsUrl(value) {
|
|
53
|
-
let url;
|
|
54
|
-
try {
|
|
55
|
-
url = new URL(value);
|
|
56
|
-
} catch {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
if (CONTROL_BYTE_RE.test(value))
|
|
60
|
-
return false;
|
|
61
|
-
return url.protocol === "https:";
|
|
62
|
-
}
|
|
63
|
-
var FIRST_PARTY_ORIGINATORS = ["codex", "codex_cli_rs", "openai", "anthropic", "claude", "claude-code"];
|
|
64
|
-
var WINTER_BRAND = Object.freeze({
|
|
65
|
-
productName: "Winter",
|
|
66
|
-
packageName: "winter-agent-sdk",
|
|
67
|
-
homeDirName: ".winter",
|
|
68
|
-
projectDirName: ".winter",
|
|
69
|
-
instructionsFile: "WINTER.md",
|
|
70
|
-
envPrefix: "WINTER_",
|
|
71
|
-
keychainService: "com.winter.core",
|
|
72
|
-
mcpServerName: "winter",
|
|
73
|
-
presetName: "winter_code",
|
|
74
|
-
processLabel: "winter",
|
|
75
|
-
codexOriginator: "winter",
|
|
76
|
-
tempRootName: "winter",
|
|
77
|
-
pluginManifestDir: ".winter-plugin",
|
|
78
|
-
contactUrl: "https://github.com/yanlingLabs/winter-agent-sdk"
|
|
79
|
-
});
|
|
80
|
-
var FIELD_RULES = [
|
|
81
|
-
{ field: "packageName", re: BRAND_TOKEN_RE, shape: "a lowercase token of 1-32 chars: a letter, then letters/digits/hyphens" },
|
|
82
|
-
{ field: "mcpServerName", re: BRAND_TOKEN_RE, shape: "a lowercase token of 1-32 chars: a letter, then letters/digits/hyphens" },
|
|
83
|
-
{ field: "processLabel", re: BRAND_TOKEN_RE, shape: "a lowercase token of 1-32 chars: a letter, then letters/digits/hyphens" },
|
|
84
|
-
{ field: "tempRootName", re: BRAND_TOKEN_RE, shape: "a lowercase token of 1-32 chars: a letter, then letters/digits/hyphens" },
|
|
85
|
-
{ field: "codexOriginator", re: BRAND_TOKEN_RE, shape: "a lowercase token of 1-32 chars: a letter, then letters/digits/hyphens" },
|
|
86
|
-
{ field: "homeDirName", re: DOT_DIR_RE, shape: "a LEADING DOT then a lowercase token (it names a hidden directory)" },
|
|
87
|
-
{ field: "projectDirName", re: DOT_DIR_RE, shape: "a LEADING DOT then a lowercase token (it names a hidden directory)" },
|
|
88
|
-
{ field: "pluginManifestDir", re: DOT_DIR_RE, shape: "a LEADING DOT then a lowercase token (it names a hidden directory)" },
|
|
89
|
-
{ field: "instructionsFile", re: INSTRUCTIONS_FILE_RE, shape: 'an UPPERCASE name with a `.md` extension, e.g. "ACME.md"' },
|
|
90
|
-
{ field: "envPrefix", re: ENV_PREFIX_RE, shape: 'an UPPERCASE prefix ENDING IN AN UNDERSCORE, e.g. "ACME_"' },
|
|
91
|
-
{ field: "keychainService", re: KEYCHAIN_SERVICE_RE, shape: 'a lowercase reverse-DNS-style service name, e.g. "com.acme.core"' }
|
|
92
|
-
];
|
|
93
|
-
function resolveBrand(partial) {
|
|
94
|
-
const p = partial ?? {};
|
|
95
|
-
const pick = (key) => p[key] === undefined ? WINTER_BRAND[key] : p[key];
|
|
96
|
-
const brand = {
|
|
97
|
-
productName: pick("productName"),
|
|
98
|
-
packageName: pick("packageName"),
|
|
99
|
-
homeDirName: pick("homeDirName"),
|
|
100
|
-
projectDirName: pick("projectDirName"),
|
|
101
|
-
instructionsFile: pick("instructionsFile"),
|
|
102
|
-
envPrefix: pick("envPrefix"),
|
|
103
|
-
keychainService: pick("keychainService"),
|
|
104
|
-
mcpServerName: pick("mcpServerName"),
|
|
105
|
-
presetName: pick("presetName"),
|
|
106
|
-
processLabel: pick("processLabel"),
|
|
107
|
-
codexOriginator: pick("codexOriginator"),
|
|
108
|
-
tempRootName: pick("tempRootName"),
|
|
109
|
-
pluginManifestDir: pick("pluginManifestDir"),
|
|
110
|
-
contactUrl: pick("contactUrl")
|
|
111
|
-
};
|
|
112
|
-
for (const key of Object.keys(brand)) {
|
|
113
|
-
if (typeof brand[key] !== "string")
|
|
114
|
-
return { ok: false, reason: `brand.${key}: expected a string, got ${brand[key] === null ? "null" : typeof brand[key]}` };
|
|
115
|
-
}
|
|
116
|
-
if (brand.productName.length === 0 || brand.productName.length > MAX_PRODUCT_NAME) {
|
|
117
|
-
return { ok: false, reason: `brand.productName: expected 1-${MAX_PRODUCT_NAME} characters, got ${brand.productName.length}` };
|
|
118
|
-
}
|
|
119
|
-
if (brand.presetName.length === 0)
|
|
120
|
-
return { ok: false, reason: "brand.presetName: expected a non-empty preset name" };
|
|
121
|
-
for (const rule of FIELD_RULES) {
|
|
122
|
-
const value = brand[rule.field];
|
|
123
|
-
if (!rule.re.test(value))
|
|
124
|
-
return { ok: false, reason: `brand.${rule.field}: ${JSON.stringify(value)} is not ${rule.shape}` };
|
|
125
|
-
}
|
|
126
|
-
if (!isHttpsUrl(brand.contactUrl)) {
|
|
127
|
-
return {
|
|
128
|
-
ok: false,
|
|
129
|
-
reason: `brand.contactUrl: ${JSON.stringify(brand.contactUrl)} is not an https:// URL — it is published to vendors in identity headers as the way to reach whoever runs this client`
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
if (FIRST_PARTY_ORIGINATORS.includes(brand.codexOriginator)) {
|
|
133
|
-
return {
|
|
134
|
-
ok: false,
|
|
135
|
-
reason: `brand.codexOriginator: ${JSON.stringify(brand.codexOriginator)} is a first-party value. ` + `The originator field names the CLIENT, and sending a vendor's own name presents this software as that vendor's tool — ` + `supply your own token instead (one of: ${FIRST_PARTY_ORIGINATORS.join(", ")} is never it).`
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
return { ok: true, brand };
|
|
139
|
-
}
|
|
140
|
-
function envName(brand, suffix) {
|
|
141
|
-
return `${brand.envPrefix}${suffix}`;
|
|
142
|
-
}
|
|
143
|
-
function mcpToolName(brand, tool) {
|
|
144
|
-
return `mcp__${brand.mcpServerName}__${tool}`;
|
|
145
|
-
}
|
|
146
|
-
function userAgent(brand, version) {
|
|
147
|
-
return `${brand.packageName}/${version}`;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
61
|
// src/options.ts
|
|
151
62
|
var SYSTEM_PROMPT_DYNAMIC_BOUNDARY = "__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__";
|
|
152
63
|
var DEFAULT_CONTEXT_WINDOW_TOKENS = 200000;
|
|
153
64
|
var DEFAULT_COMPACTION_THRESHOLD = 0.92;
|
|
154
|
-
var DEFAULT_PLANS_DIRECTORY = `${
|
|
65
|
+
var DEFAULT_PLANS_DIRECTORY = `${WINTER_BRAND2.projectDirName}/plans`;
|
|
155
66
|
var DEFAULT_OUTPUT_STYLE = "default";
|
|
156
67
|
var DEFAULT_PROVIDER_STALL_TIMEOUT_MS = 120000;
|
|
157
|
-
var DEFAULT_KEYCHAIN_SERVICE =
|
|
68
|
+
var DEFAULT_KEYCHAIN_SERVICE = WINTER_BRAND2.keychainService;
|
|
158
69
|
function isWinterMcpServerInstance(value) {
|
|
159
70
|
if (typeof value !== "object" || value === null)
|
|
160
71
|
return false;
|
|
@@ -329,32 +240,6 @@ function defaultSpawn(opts) {
|
|
|
329
240
|
};
|
|
330
241
|
}
|
|
331
242
|
|
|
332
|
-
// src/paths/home.ts
|
|
333
|
-
import { homedir } from "node:os";
|
|
334
|
-
import { join as join2 } from "node:path";
|
|
335
|
-
function isUnset(value) {
|
|
336
|
-
return value === undefined || value.trim() === "";
|
|
337
|
-
}
|
|
338
|
-
function resolveWinterHome(env, brand) {
|
|
339
|
-
const b = brand ?? WINTER_BRAND;
|
|
340
|
-
const e = env ?? process.env;
|
|
341
|
-
const override = e[envName(b, "HOME")];
|
|
342
|
-
if (!isUnset(override))
|
|
343
|
-
return override;
|
|
344
|
-
const profile = e[envName(b, "PROFILE")];
|
|
345
|
-
const dirName = profile !== undefined && profile.trim() === "dev" ? `${b.homeDirName}-dev` : b.homeDirName;
|
|
346
|
-
return join2(homedir(), dirName);
|
|
347
|
-
}
|
|
348
|
-
function resolveKeychainServiceForProfile(brand, env, hostSetKeychainService) {
|
|
349
|
-
if (hostSetKeychainService)
|
|
350
|
-
return brand.keychainService;
|
|
351
|
-
const profile = (env ?? process.env)[envName(brand, "PROFILE")];
|
|
352
|
-
if (profile === undefined || profile.trim() !== "dev")
|
|
353
|
-
return brand.keychainService;
|
|
354
|
-
const suffixed = `${brand.keychainService}.dev`;
|
|
355
|
-
return suffixed.length <= 64 ? suffixed : brand.keychainService;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
243
|
// src/protocol/messaging.ts
|
|
359
244
|
var MESSAGING_CONTROL_SUBTYPES = {
|
|
360
245
|
listReachable: "messaging.list_reachable",
|
|
@@ -637,7 +522,7 @@ function query(args) {
|
|
|
637
522
|
}
|
|
638
523
|
}
|
|
639
524
|
if (options.sessionStore !== undefined && options.persistSession === false) {
|
|
640
|
-
const homeVar =
|
|
525
|
+
const homeVar = envName2({ envPrefix: options.brand?.envPrefix ?? WINTER_BRAND2.envPrefix }, "HOME");
|
|
641
526
|
throw new Error(`sessionStore cannot be used with persistSession: false -- the storage adapter requires local writes to mirror from. Use ${homeVar}=/tmp for ephemeral local writes with external mirroring.`);
|
|
642
527
|
}
|
|
643
528
|
if (options.sessionStore !== undefined && options.enableFileCheckpointing === true) {
|
|
@@ -646,7 +531,7 @@ function query(args) {
|
|
|
646
531
|
if (options.keychainService !== undefined && options.brand?.keychainService !== undefined && options.brand.keychainService !== options.keychainService) {
|
|
647
532
|
console.error(`winter: both 'keychainService' (${options.keychainService}) and 'brand.keychainService' (${options.brand.keychainService}) are set and differ — ` + `the deprecated 'keychainService' option wins. Set only 'brand.keychainService'.`);
|
|
648
533
|
}
|
|
649
|
-
const brandResolution =
|
|
534
|
+
const brandResolution = resolveBrand2({
|
|
650
535
|
...options.brand,
|
|
651
536
|
...options.keychainService !== undefined ? { keychainService: options.keychainService } : {}
|
|
652
537
|
});
|
|
@@ -655,7 +540,7 @@ function query(args) {
|
|
|
655
540
|
const hostSetKeychainService = options.brand?.keychainService !== undefined || options.keychainService !== undefined;
|
|
656
541
|
const brand = {
|
|
657
542
|
...brandResolution.brand,
|
|
658
|
-
keychainService:
|
|
543
|
+
keychainService: resolveKeychainServiceForProfile2(brandResolution.brand, options.env, hostSetKeychainService)
|
|
659
544
|
};
|
|
660
545
|
const runtimeHooksConfig = buildRuntimeHooksConfig(options.hooks);
|
|
661
546
|
const wireMcpServers = toWireMcpServers(options.mcpServers);
|
|
@@ -711,7 +596,7 @@ function query(args) {
|
|
|
711
596
|
...options.includePartialMessages !== undefined ? { includePartialMessages: options.includePartialMessages } : {},
|
|
712
597
|
...options.maxBudgetUsd !== undefined ? { maxBudgetUsd: options.maxBudgetUsd } : {},
|
|
713
598
|
...options.providerStallTimeoutMs !== undefined ? { providerStallTimeoutMs: options.providerStallTimeoutMs } : {},
|
|
714
|
-
...brand.keychainService !==
|
|
599
|
+
...brand.keychainService !== WINTER_BRAND2.keychainService || options.keychainService !== undefined ? { keychainService: brand.keychainService } : {},
|
|
715
600
|
...options.autoClassifier !== undefined ? { autoClassifier: options.autoClassifier } : {},
|
|
716
601
|
...options.advisor !== undefined ? { advisor: options.advisor } : {},
|
|
717
602
|
brand
|
|
@@ -1075,8 +960,14 @@ function query(args) {
|
|
|
1075
960
|
};
|
|
1076
961
|
return gen;
|
|
1077
962
|
}
|
|
963
|
+
// src/version.ts
|
|
964
|
+
var SDK_VERSION = "0.0.3";
|
|
1078
965
|
// src/paths/project-key.ts
|
|
1079
|
-
var
|
|
966
|
+
var TRANSCRIPT_PROJECT_KEY_MAX_LENGTH = 64;
|
|
967
|
+
var VENDOR_PROJECT_KEY_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
968
|
+
function isVendorCompliantProjectKey(key) {
|
|
969
|
+
return VENDOR_PROJECT_KEY_PATTERN.test(key);
|
|
970
|
+
}
|
|
1080
971
|
function sanitize(absPath) {
|
|
1081
972
|
return absPath.replace(/[^a-zA-Z0-9]/g, "-");
|
|
1082
973
|
}
|
|
@@ -1092,9 +983,10 @@ function overflowSuffix(rawAbsPath) {
|
|
|
1092
983
|
}
|
|
1093
984
|
function transcriptProjectKey(absPath) {
|
|
1094
985
|
const sanitized = sanitize(absPath);
|
|
1095
|
-
if (sanitized.length <=
|
|
986
|
+
if (sanitized.length <= TRANSCRIPT_PROJECT_KEY_MAX_LENGTH)
|
|
1096
987
|
return sanitized;
|
|
1097
|
-
|
|
988
|
+
const suffix = overflowSuffix(absPath);
|
|
989
|
+
return sanitized.slice(0, TRANSCRIPT_PROJECT_KEY_MAX_LENGTH - 1 - suffix.length) + "-" + suffix;
|
|
1098
990
|
}
|
|
1099
991
|
// src/paths/keys.ts
|
|
1100
992
|
import { realpathSync } from "node:fs";
|
|
@@ -1130,590 +1022,15 @@ function compatibilityKeys(cwd) {
|
|
|
1130
1022
|
const memoryKey = commonRoot === null ? cwdKey : transcriptProjectKey(commonRoot);
|
|
1131
1023
|
return { transcriptProjectKey: cwdKey, memoryProjectKey: memoryKey, tempProjectKey: cwdKey };
|
|
1132
1024
|
}
|
|
1133
|
-
// src/store/session-store.ts
|
|
1134
|
-
import {
|
|
1135
|
-
mkdirSync,
|
|
1136
|
-
lstatSync,
|
|
1137
|
-
chmodSync,
|
|
1138
|
-
readdirSync,
|
|
1139
|
-
readFileSync as readFileSync2,
|
|
1140
|
-
statSync,
|
|
1141
|
-
openSync as openSync2,
|
|
1142
|
-
fsyncSync as fsyncSync2,
|
|
1143
|
-
closeSync as closeSync2,
|
|
1144
|
-
renameSync as renameSync2,
|
|
1145
|
-
rmSync,
|
|
1146
|
-
ftruncateSync,
|
|
1147
|
-
constants as fsConstants
|
|
1148
|
-
} from "node:fs";
|
|
1149
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1150
|
-
import { join as join3 } from "node:path";
|
|
1151
|
-
|
|
1152
|
-
// src/store/leases.ts
|
|
1153
|
-
import { openSync, readFileSync, writeSync, fsyncSync, closeSync, renameSync, linkSync, unlinkSync } from "node:fs";
|
|
1154
|
-
|
|
1155
|
-
class WinterStoreError extends Error {
|
|
1156
|
-
constructor(message) {
|
|
1157
|
-
super(message);
|
|
1158
|
-
this.name = "WinterStoreError";
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
class WinterStoreLeaseError extends WinterStoreError {
|
|
1163
|
-
heldByPid;
|
|
1164
|
-
constructor(message, heldByPid) {
|
|
1165
|
-
super(message);
|
|
1166
|
-
this.heldByPid = heldByPid;
|
|
1167
|
-
this.name = "WinterStoreLeaseError";
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
function isPidAlive(pid) {
|
|
1171
|
-
try {
|
|
1172
|
-
process.kill(pid, 0);
|
|
1173
|
-
return true;
|
|
1174
|
-
} catch (err) {
|
|
1175
|
-
const code = err.code;
|
|
1176
|
-
if (code === "ESRCH")
|
|
1177
|
-
return false;
|
|
1178
|
-
if (code === "EPERM")
|
|
1179
|
-
return true;
|
|
1180
|
-
throw err;
|
|
1181
|
-
}
|
|
1182
|
-
}
|
|
1183
|
-
function writeAllSync(fd, buf) {
|
|
1184
|
-
let written = 0;
|
|
1185
|
-
while (written < buf.length) {
|
|
1186
|
-
written += writeSync(fd, buf, written, buf.length - written);
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
function readLeaseInfo(lockPath) {
|
|
1190
|
-
let raw;
|
|
1191
|
-
try {
|
|
1192
|
-
raw = readFileSync(lockPath, "utf8");
|
|
1193
|
-
} catch (err) {
|
|
1194
|
-
if (err.code === "ENOENT")
|
|
1195
|
-
return null;
|
|
1196
|
-
throw err;
|
|
1197
|
-
}
|
|
1198
|
-
try {
|
|
1199
|
-
const parsed = JSON.parse(raw);
|
|
1200
|
-
if (typeof parsed.pid === "number" && typeof parsed.startTimeMs === "number") {
|
|
1201
|
-
return { pid: parsed.pid, startTimeMs: parsed.startTimeMs };
|
|
1202
|
-
}
|
|
1203
|
-
} catch {}
|
|
1204
|
-
return null;
|
|
1205
|
-
}
|
|
1206
|
-
function createExclusive(lockPath, info) {
|
|
1207
|
-
const data = Buffer.from(JSON.stringify(info), "utf8");
|
|
1208
|
-
const tmpPath = `${lockPath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
1209
|
-
const fd = openSync(tmpPath, "wx", 384);
|
|
1210
|
-
try {
|
|
1211
|
-
writeAllSync(fd, data);
|
|
1212
|
-
fsyncSync(fd);
|
|
1213
|
-
} finally {
|
|
1214
|
-
closeSync(fd);
|
|
1215
|
-
}
|
|
1216
|
-
try {
|
|
1217
|
-
linkSync(tmpPath, lockPath);
|
|
1218
|
-
} finally {
|
|
1219
|
-
try {
|
|
1220
|
-
unlinkSync(tmpPath);
|
|
1221
|
-
} catch (err) {
|
|
1222
|
-
if (err.code !== "ENOENT")
|
|
1223
|
-
throw err;
|
|
1224
|
-
}
|
|
1225
|
-
}
|
|
1226
|
-
}
|
|
1227
|
-
function writeLeaseInfoReplacing(lockPath, info) {
|
|
1228
|
-
const data = Buffer.from(JSON.stringify(info), "utf8");
|
|
1229
|
-
const tmpPath = `${lockPath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
1230
|
-
const fd = openSync(tmpPath, "wx", 384);
|
|
1231
|
-
try {
|
|
1232
|
-
writeAllSync(fd, data);
|
|
1233
|
-
fsyncSync(fd);
|
|
1234
|
-
} finally {
|
|
1235
|
-
closeSync(fd);
|
|
1236
|
-
}
|
|
1237
|
-
renameSync(tmpPath, lockPath);
|
|
1238
|
-
}
|
|
1239
|
-
function acquireLease(lockPath) {
|
|
1240
|
-
const fresh = { pid: process.pid, startTimeMs: Date.now() };
|
|
1241
|
-
try {
|
|
1242
|
-
createExclusive(lockPath, fresh);
|
|
1243
|
-
return fresh;
|
|
1244
|
-
} catch (err) {
|
|
1245
|
-
if (err.code !== "EEXIST")
|
|
1246
|
-
throw err;
|
|
1247
|
-
}
|
|
1248
|
-
const existing = readLeaseInfo(lockPath);
|
|
1249
|
-
if (existing !== null && existing.pid === process.pid) {
|
|
1250
|
-
return existing;
|
|
1251
|
-
}
|
|
1252
|
-
if (existing !== null && isPidAlive(existing.pid)) {
|
|
1253
|
-
throw new WinterStoreLeaseError(`session lease is held by another live process (pid ${existing.pid}); refusing a concurrent writer`, existing.pid);
|
|
1254
|
-
}
|
|
1255
|
-
writeLeaseInfoReplacing(lockPath, fresh);
|
|
1256
|
-
return fresh;
|
|
1257
|
-
}
|
|
1258
|
-
|
|
1259
|
-
// src/store/session-store.ts
|
|
1260
|
-
var DIALECT_RECORD_ENTRY_TYPE = "winter_dialect_record";
|
|
1261
|
-
var PROVIDER_STATE_FILE_SUFFIX = ".provider-state.jsonl";
|
|
1262
|
-
function assertSafeSingleSegment(value, label) {
|
|
1263
|
-
if (value === "")
|
|
1264
|
-
throw new WinterStoreError(`${label} must not be empty`);
|
|
1265
|
-
if (value.includes("/"))
|
|
1266
|
-
throw new WinterStoreError(`${label} must not contain a path separator: ${JSON.stringify(value)}`);
|
|
1267
|
-
if (value === "." || value === "..")
|
|
1268
|
-
throw new WinterStoreError(`${label} must not be a traversal segment: ${JSON.stringify(value)}`);
|
|
1269
|
-
}
|
|
1270
|
-
function assertSafeSubpath(value) {
|
|
1271
|
-
if (value === "")
|
|
1272
|
-
throw new WinterStoreError("subpath must not be empty");
|
|
1273
|
-
if (value.startsWith("/"))
|
|
1274
|
-
throw new WinterStoreError(`subpath must not be absolute: ${JSON.stringify(value)}`);
|
|
1275
|
-
const segments = value.split("/");
|
|
1276
|
-
for (const segment of segments) {
|
|
1277
|
-
if (segment === "") {
|
|
1278
|
-
throw new WinterStoreError(`subpath must not contain empty segments (double/trailing separators): ${JSON.stringify(value)}`);
|
|
1279
|
-
}
|
|
1280
|
-
if (segment === "." || segment === "..") {
|
|
1281
|
-
throw new WinterStoreError(`subpath must not contain a traversal segment: ${JSON.stringify(value)}`);
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
return segments;
|
|
1285
|
-
}
|
|
1286
|
-
function projectDir(winterHome, projectKey) {
|
|
1287
|
-
return join3(winterHome, "projects", projectKey);
|
|
1288
|
-
}
|
|
1289
|
-
function sessionStem(winterHome, projectKey, sessionId) {
|
|
1290
|
-
assertSafeSingleSegment(projectKey, "projectKey");
|
|
1291
|
-
assertSafeSingleSegment(sessionId, "sessionId");
|
|
1292
|
-
return join3(projectDir(winterHome, projectKey), sessionId);
|
|
1293
|
-
}
|
|
1294
|
-
function locateResource(winterHome, key) {
|
|
1295
|
-
assertSafeSingleSegment(key.projectKey, "projectKey");
|
|
1296
|
-
assertSafeSingleSegment(key.sessionId, "sessionId");
|
|
1297
|
-
const projDir = projectDir(winterHome, key.projectKey);
|
|
1298
|
-
const dirLevels = [winterHome, join3(winterHome, "projects"), projDir];
|
|
1299
|
-
if (key.subpath === undefined) {
|
|
1300
|
-
return { dirLevels, stem: join3(projDir, key.sessionId) };
|
|
1301
|
-
}
|
|
1302
|
-
const segments = assertSafeSubpath(key.subpath);
|
|
1303
|
-
let current = join3(projDir, key.sessionId);
|
|
1304
|
-
dirLevels.push(current);
|
|
1305
|
-
for (let i = 0;i < segments.length - 1; i++) {
|
|
1306
|
-
current = join3(current, segments[i]);
|
|
1307
|
-
dirLevels.push(current);
|
|
1308
|
-
}
|
|
1309
|
-
const stem = join3(current, segments[segments.length - 1]);
|
|
1310
|
-
return { dirLevels, stem };
|
|
1311
|
-
}
|
|
1312
|
-
function realUid() {
|
|
1313
|
-
return process.getuid();
|
|
1314
|
-
}
|
|
1315
|
-
function ensureSecureDir(path) {
|
|
1316
|
-
try {
|
|
1317
|
-
mkdirSync(path, { mode: 448 });
|
|
1318
|
-
} catch (err) {
|
|
1319
|
-
if (err.code !== "EEXIST")
|
|
1320
|
-
throw err;
|
|
1321
|
-
}
|
|
1322
|
-
const stat = lstatSync(path);
|
|
1323
|
-
if (stat.isSymbolicLink())
|
|
1324
|
-
throw new WinterStoreError(`refusing a symlink at a level the store must own: ${path}`);
|
|
1325
|
-
if (!stat.isDirectory())
|
|
1326
|
-
throw new WinterStoreError(`expected a directory, found something else at: ${path}`);
|
|
1327
|
-
if (stat.uid !== realUid())
|
|
1328
|
-
throw new WinterStoreError(`refusing a directory owned by a different uid: ${path}`);
|
|
1329
|
-
chmodSync(path, 448);
|
|
1330
|
-
}
|
|
1331
|
-
var APPEND_FLAGS = fsConstants.O_WRONLY | fsConstants.O_APPEND | fsConstants.O_CREAT | fsConstants.O_NOFOLLOW;
|
|
1332
|
-
var RW_EXISTING_FLAGS = fsConstants.O_RDWR | fsConstants.O_NOFOLLOW;
|
|
1333
|
-
function appendLinesAtomically(path, lines) {
|
|
1334
|
-
const data = Buffer.from(lines.map((l) => l + `
|
|
1335
|
-
`).join(""), "utf8");
|
|
1336
|
-
const fd = openSync2(path, APPEND_FLAGS, 384);
|
|
1337
|
-
try {
|
|
1338
|
-
writeAllSync(fd, data);
|
|
1339
|
-
fsyncSync2(fd);
|
|
1340
|
-
} finally {
|
|
1341
|
-
closeSync2(fd);
|
|
1342
|
-
}
|
|
1343
|
-
chmodSync(path, 384);
|
|
1344
|
-
}
|
|
1345
|
-
function quarantineTornTail(jsonlPath, tornRaw) {
|
|
1346
|
-
const quarantinePath = `${jsonlPath}.tail-quarantine`;
|
|
1347
|
-
const fd = openSync2(quarantinePath, APPEND_FLAGS, 384);
|
|
1348
|
-
try {
|
|
1349
|
-
writeAllSync(fd, tornRaw);
|
|
1350
|
-
fsyncSync2(fd);
|
|
1351
|
-
} finally {
|
|
1352
|
-
closeSync2(fd);
|
|
1353
|
-
}
|
|
1354
|
-
chmodSync(quarantinePath, 384);
|
|
1355
|
-
}
|
|
1356
|
-
function repairTruncate(jsonlPath, keepBytes) {
|
|
1357
|
-
const fd = openSync2(jsonlPath, RW_EXISTING_FLAGS);
|
|
1358
|
-
try {
|
|
1359
|
-
ftruncateSync(fd, keepBytes);
|
|
1360
|
-
fsyncSync2(fd);
|
|
1361
|
-
} finally {
|
|
1362
|
-
closeSync2(fd);
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
function writeJsonAtomically(path, value) {
|
|
1366
|
-
const data = Buffer.from(JSON.stringify(value), "utf8");
|
|
1367
|
-
const tmpPath = `${path}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
1368
|
-
const fd = openSync2(tmpPath, "wx", 384);
|
|
1369
|
-
try {
|
|
1370
|
-
writeAllSync(fd, data);
|
|
1371
|
-
fsyncSync2(fd);
|
|
1372
|
-
} finally {
|
|
1373
|
-
closeSync2(fd);
|
|
1374
|
-
}
|
|
1375
|
-
renameSync2(tmpPath, path);
|
|
1376
|
-
chmodSync(path, 384);
|
|
1377
|
-
}
|
|
1378
|
-
function readJsonIfExists(path) {
|
|
1379
|
-
let raw;
|
|
1380
|
-
try {
|
|
1381
|
-
raw = readFileSync2(path, "utf8");
|
|
1382
|
-
} catch (err) {
|
|
1383
|
-
if (err.code === "ENOENT")
|
|
1384
|
-
return null;
|
|
1385
|
-
throw err;
|
|
1386
|
-
}
|
|
1387
|
-
try {
|
|
1388
|
-
return JSON.parse(raw);
|
|
1389
|
-
} catch {
|
|
1390
|
-
return null;
|
|
1391
|
-
}
|
|
1392
|
-
}
|
|
1393
|
-
function rmIfExists(path) {
|
|
1394
|
-
try {
|
|
1395
|
-
rmSync(path);
|
|
1396
|
-
} catch (err) {
|
|
1397
|
-
if (err.code !== "ENOENT")
|
|
1398
|
-
throw err;
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
var NEWLINE = 10;
|
|
1402
|
-
function isParseableJson(text) {
|
|
1403
|
-
try {
|
|
1404
|
-
JSON.parse(text);
|
|
1405
|
-
return true;
|
|
1406
|
-
} catch {
|
|
1407
|
-
return false;
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
function decodeCompleteLines(buf) {
|
|
1411
|
-
if (buf.length === 0)
|
|
1412
|
-
return [];
|
|
1413
|
-
const lines = buf.toString("utf8").split(`
|
|
1414
|
-
`);
|
|
1415
|
-
lines.pop();
|
|
1416
|
-
return lines.map((line) => JSON.parse(line));
|
|
1417
|
-
}
|
|
1418
|
-
function parseWithTailRepair(buf) {
|
|
1419
|
-
if (buf.length === 0)
|
|
1420
|
-
return { entries: [], torn: null };
|
|
1421
|
-
const endsWithNewline = buf[buf.length - 1] === NEWLINE;
|
|
1422
|
-
if (endsWithNewline) {
|
|
1423
|
-
const searchEnd = buf.length - 2;
|
|
1424
|
-
const prevNL = searchEnd < 0 ? -1 : buf.lastIndexOf(NEWLINE, searchEnd);
|
|
1425
|
-
const lastLineStart = prevNL + 1;
|
|
1426
|
-
const lastLine = buf.subarray(lastLineStart, buf.length - 1).toString("utf8");
|
|
1427
|
-
if (isParseableJson(lastLine)) {
|
|
1428
|
-
return { entries: decodeCompleteLines(buf), torn: null };
|
|
1429
|
-
}
|
|
1430
|
-
const keepBytes = lastLineStart;
|
|
1431
|
-
const tornRaw = Buffer.from(buf.subarray(lastLineStart));
|
|
1432
|
-
return { entries: decodeCompleteLines(buf.subarray(0, keepBytes)), torn: { raw: tornRaw, keepBytes } };
|
|
1433
|
-
}
|
|
1434
|
-
const lastNL = buf.lastIndexOf(NEWLINE);
|
|
1435
|
-
const keepBytes = lastNL + 1;
|
|
1436
|
-
const tornRaw = Buffer.from(buf.subarray(keepBytes));
|
|
1437
|
-
return { entries: decodeCompleteLines(buf.subarray(0, keepBytes)), torn: { raw: tornRaw, keepBytes } };
|
|
1438
|
-
}
|
|
1439
|
-
function hasLiveForeignLeaseHolder(lockPath) {
|
|
1440
|
-
const lease = readLeaseInfo(lockPath);
|
|
1441
|
-
return lease !== null && lease.pid !== process.pid && isPidAlive(lease.pid);
|
|
1442
|
-
}
|
|
1443
|
-
function walkResourceStems(dir, prefix, out) {
|
|
1444
|
-
let entries;
|
|
1445
|
-
try {
|
|
1446
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
1447
|
-
} catch (err) {
|
|
1448
|
-
if (err.code === "ENOENT")
|
|
1449
|
-
return;
|
|
1450
|
-
throw err;
|
|
1451
|
-
}
|
|
1452
|
-
for (const dirent of entries) {
|
|
1453
|
-
const relPath = prefix === "" ? dirent.name : `${prefix}/${dirent.name}`;
|
|
1454
|
-
if (dirent.isDirectory()) {
|
|
1455
|
-
walkResourceStems(join3(dir, dirent.name), relPath, out);
|
|
1456
|
-
} else if (dirent.isFile() && dirent.name.endsWith(".jsonl")) {
|
|
1457
|
-
out.add(relPath.slice(0, -".jsonl".length));
|
|
1458
|
-
} else if (dirent.isFile() && dirent.name.endsWith(".meta.json")) {
|
|
1459
|
-
out.add(relPath.slice(0, -".meta.json".length));
|
|
1460
|
-
}
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
|
-
function foldSummary(summaryPath, sessionId, newEntries, dialectExtra) {
|
|
1464
|
-
const previous = readJsonIfExists(summaryPath) ?? {};
|
|
1465
|
-
const mechanical = {};
|
|
1466
|
-
if (newEntries.length > 0) {
|
|
1467
|
-
const lastEntry = newEntries[newEntries.length - 1];
|
|
1468
|
-
mechanical.entryCount = (previous.entryCount ?? 0) + newEntries.length;
|
|
1469
|
-
mechanical.lastEntryType = lastEntry.type;
|
|
1470
|
-
if (lastEntry.timestamp !== undefined)
|
|
1471
|
-
mechanical.lastTimestamp = lastEntry.timestamp;
|
|
1472
|
-
}
|
|
1473
|
-
const updated = {
|
|
1474
|
-
...previous,
|
|
1475
|
-
...dialectExtra,
|
|
1476
|
-
sessionId,
|
|
1477
|
-
...mechanical,
|
|
1478
|
-
mtime: Date.now()
|
|
1479
|
-
};
|
|
1480
|
-
writeJsonAtomically(summaryPath, updated);
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
class WinterCompatibilitySessionStore {
|
|
1484
|
-
winterHome;
|
|
1485
|
-
constructor(opts) {
|
|
1486
|
-
this.winterHome = opts.winterHome;
|
|
1487
|
-
}
|
|
1488
|
-
async append(key, entries) {
|
|
1489
|
-
if (entries.length === 0)
|
|
1490
|
-
return;
|
|
1491
|
-
const { dirLevels, stem } = locateResource(this.winterHome, key);
|
|
1492
|
-
for (const level of dirLevels)
|
|
1493
|
-
ensureSecureDir(level);
|
|
1494
|
-
const lockPath = `${sessionStem(this.winterHome, key.projectKey, key.sessionId)}.lock`;
|
|
1495
|
-
acquireLease(lockPath);
|
|
1496
|
-
chmodSync(lockPath, 384);
|
|
1497
|
-
const jsonlPath = `${stem}.jsonl`;
|
|
1498
|
-
const metaPath = `${stem}.meta.json`;
|
|
1499
|
-
const nativeEntries = [];
|
|
1500
|
-
let latestMetadata;
|
|
1501
|
-
let dialectExtra;
|
|
1502
|
-
for (const e of entries) {
|
|
1503
|
-
if (e.type === "agent_metadata") {
|
|
1504
|
-
latestMetadata = e;
|
|
1505
|
-
} else if (e.type === DIALECT_RECORD_ENTRY_TYPE) {
|
|
1506
|
-
const { type: _type, ...fields } = e;
|
|
1507
|
-
dialectExtra = fields;
|
|
1508
|
-
} else {
|
|
1509
|
-
nativeEntries.push(e);
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
if (nativeEntries.length > 0) {
|
|
1513
|
-
appendLinesAtomically(jsonlPath, nativeEntries.map((e) => JSON.stringify(e)));
|
|
1514
|
-
}
|
|
1515
|
-
if (latestMetadata !== undefined) {
|
|
1516
|
-
writeJsonAtomically(metaPath, latestMetadata);
|
|
1517
|
-
}
|
|
1518
|
-
if (key.subpath === undefined && (nativeEntries.length > 0 || dialectExtra !== undefined)) {
|
|
1519
|
-
foldSummary(`${sessionStem(this.winterHome, key.projectKey, key.sessionId)}.summary.json`, key.sessionId, nativeEntries, dialectExtra);
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
async load(key) {
|
|
1523
|
-
const { stem } = locateResource(this.winterHome, key);
|
|
1524
|
-
const jsonlPath = `${stem}.jsonl`;
|
|
1525
|
-
const metaPath = `${stem}.meta.json`;
|
|
1526
|
-
let raw;
|
|
1527
|
-
try {
|
|
1528
|
-
raw = readFileSync2(jsonlPath);
|
|
1529
|
-
} catch (err) {
|
|
1530
|
-
if (err.code !== "ENOENT")
|
|
1531
|
-
throw err;
|
|
1532
|
-
raw = null;
|
|
1533
|
-
}
|
|
1534
|
-
if (raw === null) {
|
|
1535
|
-
const meta = readJsonIfExists(metaPath);
|
|
1536
|
-
return meta === null ? null : [meta];
|
|
1537
|
-
}
|
|
1538
|
-
const { entries, torn } = parseWithTailRepair(raw);
|
|
1539
|
-
if (torn !== null) {
|
|
1540
|
-
const lockPath = `${sessionStem(this.winterHome, key.projectKey, key.sessionId)}.lock`;
|
|
1541
|
-
if (!hasLiveForeignLeaseHolder(lockPath)) {
|
|
1542
|
-
quarantineTornTail(jsonlPath, torn.raw);
|
|
1543
|
-
repairTruncate(jsonlPath, torn.keepBytes);
|
|
1544
|
-
}
|
|
1545
|
-
}
|
|
1546
|
-
const meta = readJsonIfExists(metaPath);
|
|
1547
|
-
if (meta !== null)
|
|
1548
|
-
entries.push(meta);
|
|
1549
|
-
return entries;
|
|
1550
|
-
}
|
|
1551
|
-
async listSessions(projectKey) {
|
|
1552
|
-
assertSafeSingleSegment(projectKey, "projectKey");
|
|
1553
|
-
const dir = projectDir(this.winterHome, projectKey);
|
|
1554
|
-
let names;
|
|
1555
|
-
try {
|
|
1556
|
-
names = readdirSync(dir);
|
|
1557
|
-
} catch (err) {
|
|
1558
|
-
if (err.code === "ENOENT")
|
|
1559
|
-
return [];
|
|
1560
|
-
throw err;
|
|
1561
|
-
}
|
|
1562
|
-
const result = [];
|
|
1563
|
-
const seenSessionIds = new Set;
|
|
1564
|
-
for (const name of names) {
|
|
1565
|
-
if (!name.endsWith(".jsonl"))
|
|
1566
|
-
continue;
|
|
1567
|
-
const full = join3(dir, name);
|
|
1568
|
-
const stat = statSync(full);
|
|
1569
|
-
if (!stat.isFile())
|
|
1570
|
-
continue;
|
|
1571
|
-
const sessionId = name.slice(0, -".jsonl".length);
|
|
1572
|
-
seenSessionIds.add(sessionId);
|
|
1573
|
-
result.push({ sessionId, mtime: stat.mtimeMs });
|
|
1574
|
-
}
|
|
1575
|
-
for (const name of names) {
|
|
1576
|
-
if (!name.endsWith(".meta.json"))
|
|
1577
|
-
continue;
|
|
1578
|
-
const sessionId = name.slice(0, -".meta.json".length);
|
|
1579
|
-
if (seenSessionIds.has(sessionId))
|
|
1580
|
-
continue;
|
|
1581
|
-
const full = join3(dir, name);
|
|
1582
|
-
const stat = statSync(full);
|
|
1583
|
-
if (!stat.isFile())
|
|
1584
|
-
continue;
|
|
1585
|
-
seenSessionIds.add(sessionId);
|
|
1586
|
-
result.push({ sessionId, mtime: stat.mtimeMs });
|
|
1587
|
-
}
|
|
1588
|
-
return result;
|
|
1589
|
-
}
|
|
1590
|
-
async listSessionSummaries(projectKey) {
|
|
1591
|
-
assertSafeSingleSegment(projectKey, "projectKey");
|
|
1592
|
-
const dir = projectDir(this.winterHome, projectKey);
|
|
1593
|
-
let names;
|
|
1594
|
-
try {
|
|
1595
|
-
names = readdirSync(dir);
|
|
1596
|
-
} catch (err) {
|
|
1597
|
-
if (err.code === "ENOENT")
|
|
1598
|
-
return [];
|
|
1599
|
-
throw err;
|
|
1600
|
-
}
|
|
1601
|
-
const result = [];
|
|
1602
|
-
for (const name of names) {
|
|
1603
|
-
if (!name.endsWith(".summary.json"))
|
|
1604
|
-
continue;
|
|
1605
|
-
const parsed = readJsonIfExists(join3(dir, name));
|
|
1606
|
-
if (parsed !== null)
|
|
1607
|
-
result.push(parsed);
|
|
1608
|
-
}
|
|
1609
|
-
return result;
|
|
1610
|
-
}
|
|
1611
|
-
async delete(key) {
|
|
1612
|
-
const { stem } = locateResource(this.winterHome, key);
|
|
1613
|
-
if (key.subpath === undefined) {
|
|
1614
|
-
rmIfExists(`${stem}.jsonl`);
|
|
1615
|
-
rmIfExists(`${stem}.jsonl.tail-quarantine`);
|
|
1616
|
-
rmIfExists(`${stem}.lock`);
|
|
1617
|
-
rmIfExists(`${stem}.summary.json`);
|
|
1618
|
-
rmIfExists(`${stem}.meta.json`);
|
|
1619
|
-
rmIfExists(`${stem}${PROVIDER_STATE_FILE_SUFFIX}`);
|
|
1620
|
-
rmSync(stem, { recursive: true, force: true });
|
|
1621
|
-
} else {
|
|
1622
|
-
rmIfExists(`${stem}.jsonl`);
|
|
1623
|
-
rmIfExists(`${stem}.jsonl.tail-quarantine`);
|
|
1624
|
-
rmIfExists(`${stem}.meta.json`);
|
|
1625
|
-
rmIfExists(`${stem}${PROVIDER_STATE_FILE_SUFFIX}`);
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1628
|
-
async listSubkeys(key) {
|
|
1629
|
-
const sessionDir = sessionStem(this.winterHome, key.projectKey, key.sessionId);
|
|
1630
|
-
const results = new Set;
|
|
1631
|
-
walkResourceStems(sessionDir, "", results);
|
|
1632
|
-
return [...results];
|
|
1633
|
-
}
|
|
1634
|
-
async listProjectKeys() {
|
|
1635
|
-
const dir = join3(this.winterHome, "projects");
|
|
1636
|
-
let entries;
|
|
1637
|
-
try {
|
|
1638
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
1639
|
-
} catch (err) {
|
|
1640
|
-
if (err.code === "ENOENT")
|
|
1641
|
-
return [];
|
|
1642
|
-
throw err;
|
|
1643
|
-
}
|
|
1644
|
-
return entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
1645
|
-
}
|
|
1646
|
-
async copyProviderStateForFork(src, dest) {
|
|
1647
|
-
const { stem: srcStem } = locateResource(this.winterHome, src);
|
|
1648
|
-
const { stem: destStem, dirLevels } = locateResource(this.winterHome, dest);
|
|
1649
|
-
let raw;
|
|
1650
|
-
try {
|
|
1651
|
-
raw = readFileSync2(`${srcStem}${PROVIDER_STATE_FILE_SUFFIX}`, "utf8");
|
|
1652
|
-
} catch (err) {
|
|
1653
|
-
if (err.code === "ENOENT")
|
|
1654
|
-
return 0;
|
|
1655
|
-
throw err;
|
|
1656
|
-
}
|
|
1657
|
-
const lines = [];
|
|
1658
|
-
for (const line of raw.split(`
|
|
1659
|
-
`)) {
|
|
1660
|
-
if (line.length === 0)
|
|
1661
|
-
continue;
|
|
1662
|
-
let record;
|
|
1663
|
-
try {
|
|
1664
|
-
record = JSON.parse(line);
|
|
1665
|
-
} catch {
|
|
1666
|
-
continue;
|
|
1667
|
-
}
|
|
1668
|
-
if (typeof record !== "object" || record === null || Array.isArray(record))
|
|
1669
|
-
continue;
|
|
1670
|
-
lines.push(JSON.stringify({ ...record, uuid: randomUUID2(), sessionId: dest.sessionId }));
|
|
1671
|
-
}
|
|
1672
|
-
if (lines.length === 0)
|
|
1673
|
-
return 0;
|
|
1674
|
-
for (const level of dirLevels)
|
|
1675
|
-
ensureSecureDir(level);
|
|
1676
|
-
appendLinesAtomically(`${destStem}${PROVIDER_STATE_FILE_SUFFIX}`, lines);
|
|
1677
|
-
return lines.length;
|
|
1678
|
-
}
|
|
1679
|
-
async readSessionSummary(key) {
|
|
1680
|
-
assertSafeSingleSegment(key.projectKey, "projectKey");
|
|
1681
|
-
assertSafeSingleSegment(key.sessionId, "sessionId");
|
|
1682
|
-
return readJsonIfExists(`${sessionStem(this.winterHome, key.projectKey, key.sessionId)}.summary.json`);
|
|
1683
|
-
}
|
|
1684
|
-
async mergeSessionMetadata(key, patch) {
|
|
1685
|
-
const stem = sessionStem(this.winterHome, key.projectKey, key.sessionId);
|
|
1686
|
-
const lockPath = `${stem}.lock`;
|
|
1687
|
-
acquireLease(lockPath);
|
|
1688
|
-
chmodSync(lockPath, 384);
|
|
1689
|
-
const summaryPath = `${stem}.summary.json`;
|
|
1690
|
-
const previous = readJsonIfExists(summaryPath) ?? {};
|
|
1691
|
-
const updated = {
|
|
1692
|
-
...previous,
|
|
1693
|
-
...patch,
|
|
1694
|
-
sessionId: key.sessionId,
|
|
1695
|
-
mtime: Date.now()
|
|
1696
|
-
};
|
|
1697
|
-
writeJsonAtomically(summaryPath, updated);
|
|
1698
|
-
}
|
|
1699
|
-
async acquireSessionLease(key) {
|
|
1700
|
-
const { dirLevels } = locateResource(this.winterHome, key);
|
|
1701
|
-
for (const level of dirLevels)
|
|
1702
|
-
ensureSecureDir(level);
|
|
1703
|
-
const lockPath = `${sessionStem(this.winterHome, key.projectKey, key.sessionId)}.lock`;
|
|
1704
|
-
acquireLease(lockPath);
|
|
1705
|
-
chmodSync(lockPath, 384);
|
|
1706
|
-
}
|
|
1707
|
-
}
|
|
1708
1025
|
// src/store/fork-session.ts
|
|
1709
|
-
import { randomUUID as
|
|
1026
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
1710
1027
|
var IDENTITY_FIELDS = ["providerId", "modelKey", "adapterId", "adapterVersion", "catalogVersion", "authRef", "classifierPin"];
|
|
1711
1028
|
async function forkSessionByKey(store, src) {
|
|
1712
1029
|
const entries = await store.load(src);
|
|
1713
1030
|
if (entries === null) {
|
|
1714
1031
|
throw new SessionNotFoundError("not_found", `forkSession: source session not found: ${JSON.stringify(src)}`);
|
|
1715
1032
|
}
|
|
1716
|
-
const newSessionId =
|
|
1033
|
+
const newSessionId = randomUUID2();
|
|
1717
1034
|
const rewritten = entries.map((e) => typeof e.sessionId === "string" ? { ...e, sessionId: newSessionId } : e);
|
|
1718
1035
|
const destKey = { projectKey: src.projectKey, sessionId: newSessionId, ...src.subpath !== undefined ? { subpath: src.subpath } : {} };
|
|
1719
1036
|
await store.append(destKey, rewritten);
|
|
@@ -1722,7 +1039,7 @@ async function forkSessionByKey(store, src) {
|
|
|
1722
1039
|
await carry.copyProviderStateForFork?.(src, destKey);
|
|
1723
1040
|
const summary = destKey.subpath === undefined && carry.readSessionSummary !== undefined ? await carry.readSessionSummary({ projectKey: src.projectKey, sessionId: src.sessionId }) : null;
|
|
1724
1041
|
if (summary !== null && typeof summary.providerId === "string" && typeof summary.modelKey === "string") {
|
|
1725
|
-
const identity = { type:
|
|
1042
|
+
const identity = { type: DIALECT_RECORD_ENTRY_TYPE2 };
|
|
1726
1043
|
for (const field of IDENTITY_FIELDS) {
|
|
1727
1044
|
const value = summary[field];
|
|
1728
1045
|
if (typeof value === "string")
|
|
@@ -1738,14 +1055,14 @@ function resolveHome(winterHome, brand) {
|
|
|
1738
1055
|
if (winterHome !== undefined)
|
|
1739
1056
|
return winterHome;
|
|
1740
1057
|
if (brand === undefined)
|
|
1741
|
-
return
|
|
1742
|
-
const resolved =
|
|
1058
|
+
return resolveWinterHome2();
|
|
1059
|
+
const resolved = resolveBrand2(brand);
|
|
1743
1060
|
if (!resolved.ok)
|
|
1744
1061
|
throw new TypeError(`sessions: invalid brand profile -- ${resolved.reason}`);
|
|
1745
|
-
return
|
|
1062
|
+
return resolveWinterHome2(undefined, resolved.brand);
|
|
1746
1063
|
}
|
|
1747
1064
|
function openStore(opts) {
|
|
1748
|
-
return new
|
|
1065
|
+
return new WinterCompatibilitySessionStore2({ winterHome: resolveHome(opts?.winterHome, opts?.brand) });
|
|
1749
1066
|
}
|
|
1750
1067
|
async function findInProject(store, projectKey, sessionId) {
|
|
1751
1068
|
const sessions = await store.listSessions(projectKey);
|
|
@@ -1860,16 +1177,16 @@ async function getSubagentMessages(sessionId, agentId, opts) {
|
|
|
1860
1177
|
}
|
|
1861
1178
|
// src/settings/sources.ts
|
|
1862
1179
|
import { readFile } from "node:fs/promises";
|
|
1863
|
-
import { join as
|
|
1180
|
+
import { join as join2 } from "node:path";
|
|
1864
1181
|
function settingsPathFor(source, opts) {
|
|
1865
|
-
const brand = opts.brand ??
|
|
1182
|
+
const brand = opts.brand ?? WINTER_BRAND2;
|
|
1866
1183
|
switch (source) {
|
|
1867
1184
|
case "user":
|
|
1868
|
-
return
|
|
1185
|
+
return join2(opts.winterHome ?? resolveWinterHome2(opts.env, brand), "settings.json");
|
|
1869
1186
|
case "project":
|
|
1870
|
-
return
|
|
1187
|
+
return join2(opts.cwd, brand.projectDirName, "settings.json");
|
|
1871
1188
|
case "local":
|
|
1872
|
-
return
|
|
1189
|
+
return join2(opts.cwd, brand.projectDirName, "settings.local.json");
|
|
1873
1190
|
}
|
|
1874
1191
|
}
|
|
1875
1192
|
function isPlainObject(v) {
|
|
@@ -2337,7 +1654,7 @@ var HOOK_EVENTS = [
|
|
|
2337
1654
|
];
|
|
2338
1655
|
export {
|
|
2339
1656
|
AbortError,
|
|
2340
|
-
BRAND_TOKEN_RE,
|
|
1657
|
+
BRAND_TOKEN_RE2 as BRAND_TOKEN_RE,
|
|
2341
1658
|
CLIConnectionError,
|
|
2342
1659
|
DEFAULT_COMPACTION_THRESHOLD,
|
|
2343
1660
|
DEFAULT_CONTEXT_WINDOW_TOKENS,
|
|
@@ -2345,9 +1662,9 @@ export {
|
|
|
2345
1662
|
DEFAULT_OUTPUT_STYLE,
|
|
2346
1663
|
DEFAULT_PLANS_DIRECTORY,
|
|
2347
1664
|
DEFAULT_PROVIDER_STALL_TIMEOUT_MS,
|
|
2348
|
-
DIALECT_RECORD_ENTRY_TYPE,
|
|
1665
|
+
DIALECT_RECORD_ENTRY_TYPE2 as DIALECT_RECORD_ENTRY_TYPE,
|
|
2349
1666
|
ESCALATING_PERMISSION_MODES,
|
|
2350
|
-
FIRST_PARTY_ORIGINATORS,
|
|
1667
|
+
FIRST_PARTY_ORIGINATORS2 as FIRST_PARTY_ORIGINATORS,
|
|
2351
1668
|
HOOK_EVENTS,
|
|
2352
1669
|
InvalidBrandError,
|
|
2353
1670
|
MESSAGING_CONTROL_SUBTYPES,
|
|
@@ -2357,28 +1674,30 @@ export {
|
|
|
2357
1674
|
OVERLAY_NEVER_KEYS,
|
|
2358
1675
|
PROJECT_PERMISSIVE_KEYS,
|
|
2359
1676
|
PROTOCOL_VERSION,
|
|
2360
|
-
PROVIDER_STATE_FILE_SUFFIX,
|
|
1677
|
+
PROVIDER_STATE_FILE_SUFFIX2 as PROVIDER_STATE_FILE_SUFFIX,
|
|
2361
1678
|
ProcessError,
|
|
2362
1679
|
ProtocolDecodeError,
|
|
2363
1680
|
ProtocolError,
|
|
2364
1681
|
ResultError,
|
|
1682
|
+
SDK_VERSION,
|
|
2365
1683
|
SETTING_SOURCES,
|
|
2366
1684
|
SYSTEM_PROMPT_DYNAMIC_BOUNDARY,
|
|
2367
1685
|
SessionNotFoundError,
|
|
2368
|
-
|
|
2369
|
-
|
|
1686
|
+
TRANSCRIPT_PROJECT_KEY_MAX_LENGTH,
|
|
1687
|
+
WINTER_BRAND2 as WINTER_BRAND,
|
|
1688
|
+
WinterCompatibilitySessionStore2 as WinterCompatibilitySessionStore,
|
|
2370
1689
|
WinterRpcError,
|
|
2371
1690
|
WinterRpcTimeoutError,
|
|
2372
1691
|
WinterSDKError,
|
|
2373
|
-
WinterStoreError,
|
|
2374
|
-
WinterStoreLeaseError,
|
|
1692
|
+
WinterStoreError2 as WinterStoreError,
|
|
1693
|
+
WinterStoreLeaseError2 as WinterStoreLeaseError,
|
|
2375
1694
|
applyWorkspaceTrust,
|
|
2376
1695
|
compatibilityKeys,
|
|
2377
1696
|
decodeFrame,
|
|
2378
1697
|
defaultSpawn,
|
|
2379
1698
|
deleteSession,
|
|
2380
1699
|
encodeFrame,
|
|
2381
|
-
envName,
|
|
1700
|
+
envName2 as envName,
|
|
2382
1701
|
filterEscalatingDefaultMode,
|
|
2383
1702
|
forkSession,
|
|
2384
1703
|
forkSessionByKey,
|
|
@@ -2397,26 +1716,27 @@ export {
|
|
|
2397
1716
|
isNotificationRecord,
|
|
2398
1717
|
isPermissionClassLabel,
|
|
2399
1718
|
isRuntimeAddress,
|
|
2400
|
-
isUnset,
|
|
1719
|
+
isUnset2 as isUnset,
|
|
1720
|
+
isVendorCompliantProjectKey,
|
|
2401
1721
|
isWinterMcpServerInstance,
|
|
2402
1722
|
listSessions,
|
|
2403
1723
|
listSubagents,
|
|
2404
1724
|
loadSettingsFile,
|
|
2405
|
-
mcpToolName,
|
|
1725
|
+
mcpToolName2 as mcpToolName,
|
|
2406
1726
|
providerSettingsFrom,
|
|
2407
1727
|
query,
|
|
2408
1728
|
renameSession,
|
|
2409
|
-
resolveBrand,
|
|
1729
|
+
resolveBrand2 as resolveBrand,
|
|
2410
1730
|
resolveFacetTarget,
|
|
2411
|
-
resolveKeychainServiceForProfile,
|
|
1731
|
+
resolveKeychainServiceForProfile2 as resolveKeychainServiceForProfile,
|
|
2412
1732
|
resolveRuntimeExecutable,
|
|
2413
1733
|
resolveSettings,
|
|
2414
1734
|
resolveSettingsDetailed,
|
|
2415
|
-
resolveWinterHome,
|
|
1735
|
+
resolveWinterHome2 as resolveWinterHome,
|
|
2416
1736
|
settingsPathFor,
|
|
2417
1737
|
splitFrames,
|
|
2418
1738
|
tagSession,
|
|
2419
1739
|
transcriptProjectKey,
|
|
2420
|
-
userAgent,
|
|
1740
|
+
userAgent2 as userAgent,
|
|
2421
1741
|
validateModelSlots
|
|
2422
1742
|
};
|