zam-core 0.3.12 → 0.4.0
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/.agents/skills/zam/SKILL.md +97 -5
- package/dist/cli/index.js +1278 -377
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +148 -2
- package/dist/index.js +538 -204
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -690,6 +690,66 @@ function isRemoteDatabasePath(dbPath) {
|
|
|
690
690
|
function isDatabaseProvider(value) {
|
|
691
691
|
return value === "local" || value === "native" || value === "remote";
|
|
692
692
|
}
|
|
693
|
+
function cwdRequiresTursoCredentials() {
|
|
694
|
+
try {
|
|
695
|
+
const configPath = join2(process.cwd(), ".zam", "config.yaml");
|
|
696
|
+
if (existsSync2(configPath)) {
|
|
697
|
+
const configText = readFileSync2(configPath, "utf-8");
|
|
698
|
+
return /[\s\S]*turso:[\s\S]*url:/m.test(configText);
|
|
699
|
+
}
|
|
700
|
+
} catch (_e) {
|
|
701
|
+
}
|
|
702
|
+
return false;
|
|
703
|
+
}
|
|
704
|
+
function resolveDatabaseTarget(options = {}) {
|
|
705
|
+
const configuredCloud = options.useConfiguredCloud !== false && !options.dbPath && !options.syncUrl ? getTursoCredentials() : null;
|
|
706
|
+
if (cwdRequiresTursoCredentials() && !configuredCloud && options.useConfiguredCloud !== false && !options.dbPath && !options.syncUrl) {
|
|
707
|
+
throw new Error(
|
|
708
|
+
"Turso cloud database is configured in .zam/config.yaml but missing local credentials. Run: zam connector setup turso"
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
const dbPath = configuredCloud?.url ?? options.dbPath ?? DEFAULT_DB_PATH;
|
|
712
|
+
const isRemote = isRemoteDatabasePath(dbPath);
|
|
713
|
+
const isEmbeddedReplica = Boolean(options.syncUrl);
|
|
714
|
+
const provider = resolveProvider(options, configuredCloud?.mode, isRemote);
|
|
715
|
+
return {
|
|
716
|
+
dbPath,
|
|
717
|
+
provider,
|
|
718
|
+
isRemote,
|
|
719
|
+
isEmbeddedReplica,
|
|
720
|
+
configuredCloud
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
function getDatabaseTargetInfo(options = {}) {
|
|
724
|
+
const target = resolveDatabaseTarget(options);
|
|
725
|
+
if (target.isEmbeddedReplica) {
|
|
726
|
+
return {
|
|
727
|
+
kind: "turso-replica",
|
|
728
|
+
provider: target.provider,
|
|
729
|
+
location: target.dbPath,
|
|
730
|
+
syncUrl: options.syncUrl
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
if (target.provider === "remote" && target.isRemote) {
|
|
734
|
+
return {
|
|
735
|
+
kind: "turso-remote",
|
|
736
|
+
provider: target.provider,
|
|
737
|
+
location: target.dbPath
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
if (target.isRemote) {
|
|
741
|
+
return {
|
|
742
|
+
kind: "turso-native",
|
|
743
|
+
provider: target.provider,
|
|
744
|
+
location: target.dbPath
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
return {
|
|
748
|
+
kind: "local",
|
|
749
|
+
provider: target.provider,
|
|
750
|
+
location: target.dbPath
|
|
751
|
+
};
|
|
752
|
+
}
|
|
693
753
|
function openLocalSqlite(dbPath) {
|
|
694
754
|
const mod = require2("better-sqlite3");
|
|
695
755
|
const BetterSqlite3 = "default" in mod ? mod.default : mod;
|
|
@@ -707,27 +767,7 @@ function loadLibsql() {
|
|
|
707
767
|
}
|
|
708
768
|
}
|
|
709
769
|
async function openDatabase(options = {}) {
|
|
710
|
-
const
|
|
711
|
-
let requiresTurso = false;
|
|
712
|
-
try {
|
|
713
|
-
const configPath = join2(process.cwd(), ".zam", "config.yaml");
|
|
714
|
-
if (existsSync2(configPath)) {
|
|
715
|
-
const configText = readFileSync2(configPath, "utf-8");
|
|
716
|
-
if (/[\s\S]*turso:[\s\S]*url:/m.test(configText)) {
|
|
717
|
-
requiresTurso = true;
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
} catch (_e) {
|
|
721
|
-
}
|
|
722
|
-
if (requiresTurso && !configuredCloud && options.useConfiguredCloud !== false && !options.dbPath && !options.syncUrl) {
|
|
723
|
-
throw new Error(
|
|
724
|
-
"Turso cloud database is configured in .zam/config.yaml but missing local credentials. Run: zam connector setup turso"
|
|
725
|
-
);
|
|
726
|
-
}
|
|
727
|
-
const dbPath = configuredCloud?.url ?? options.dbPath ?? DEFAULT_DB_PATH;
|
|
728
|
-
const isRemote = isRemoteDatabasePath(dbPath);
|
|
729
|
-
const isEmbeddedReplica = Boolean(options.syncUrl);
|
|
730
|
-
const provider = resolveProvider(options, configuredCloud?.mode, isRemote);
|
|
770
|
+
const { dbPath, provider, isRemote, isEmbeddedReplica, configuredCloud } = resolveDatabaseTarget(options);
|
|
731
771
|
const shouldInitialize = options.initialize === true || !isRemote && !isEmbeddedReplica && !existsSync2(dbPath);
|
|
732
772
|
if (provider === "remote") {
|
|
733
773
|
const url = isRemote ? dbPath : options.syncUrl;
|
|
@@ -1347,8 +1387,17 @@ async function deleteCardForUser(db, tokenId, userId) {
|
|
|
1347
1387
|
await db.prepare("DELETE FROM cards WHERE id = ?").run(card.id);
|
|
1348
1388
|
return { card, impact };
|
|
1349
1389
|
}
|
|
1350
|
-
async function getDueCards(db, userId, now) {
|
|
1390
|
+
async function getDueCards(db, userId, now, domain) {
|
|
1351
1391
|
const cutoff = now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1392
|
+
if (domain) {
|
|
1393
|
+
return await db.prepare(
|
|
1394
|
+
`SELECT c.*, t.slug, t.concept, t.domain, t.bloom_level
|
|
1395
|
+
FROM cards c
|
|
1396
|
+
JOIN tokens t ON t.id = c.token_id
|
|
1397
|
+
WHERE c.user_id = ? AND c.blocked = 0 AND c.due_at <= ? AND t.domain = ?
|
|
1398
|
+
ORDER BY t.bloom_level ASC, c.due_at ASC`
|
|
1399
|
+
).all(userId, cutoff, domain);
|
|
1400
|
+
}
|
|
1352
1401
|
return await db.prepare(
|
|
1353
1402
|
`SELECT c.*, t.slug, t.concept, t.domain, t.bloom_level
|
|
1354
1403
|
FROM cards c
|
|
@@ -2121,6 +2170,415 @@ function getMonitorLogStats(sessionId) {
|
|
|
2121
2170
|
return { exists: true, sizeBytes: stat.size, lineCount };
|
|
2122
2171
|
}
|
|
2123
2172
|
|
|
2173
|
+
// src/kernel/observation/observer-sidecar-policy.ts
|
|
2174
|
+
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
2175
|
+
import { join as join6 } from "path";
|
|
2176
|
+
|
|
2177
|
+
// src/kernel/observation/policy.ts
|
|
2178
|
+
var OBSERVER_POLICY_VERSION = 1;
|
|
2179
|
+
var DEFAULT_OBSERVER_POLICY = {
|
|
2180
|
+
version: OBSERVER_POLICY_VERSION,
|
|
2181
|
+
scope: "window",
|
|
2182
|
+
allowlist: [],
|
|
2183
|
+
denylist: [],
|
|
2184
|
+
consent: "per-session",
|
|
2185
|
+
retention: "none",
|
|
2186
|
+
redactWindowTitles: true,
|
|
2187
|
+
audioOptIn: false
|
|
2188
|
+
};
|
|
2189
|
+
var BUILT_IN_SENSITIVE_MATCHERS = [
|
|
2190
|
+
// Password managers
|
|
2191
|
+
"1password",
|
|
2192
|
+
"bitwarden",
|
|
2193
|
+
"keepass",
|
|
2194
|
+
"lastpass",
|
|
2195
|
+
"dashlane",
|
|
2196
|
+
"nordpass",
|
|
2197
|
+
"enpass",
|
|
2198
|
+
"proton pass",
|
|
2199
|
+
// Authentication / credential / UAC surfaces
|
|
2200
|
+
"credentialuibroker",
|
|
2201
|
+
"consentux",
|
|
2202
|
+
"logonui",
|
|
2203
|
+
"windowssecurity",
|
|
2204
|
+
"authenticator",
|
|
2205
|
+
// Conservative banking/title hints
|
|
2206
|
+
"online banking",
|
|
2207
|
+
"onlinebanking"
|
|
2208
|
+
];
|
|
2209
|
+
function parseList(raw) {
|
|
2210
|
+
if (!raw) return [];
|
|
2211
|
+
return raw.split(",").map((entry) => entry.trim().toLowerCase()).filter((entry) => entry.length > 0);
|
|
2212
|
+
}
|
|
2213
|
+
function parseObserverList(raw) {
|
|
2214
|
+
return parseList(raw);
|
|
2215
|
+
}
|
|
2216
|
+
function parseScope(raw, defaultScope) {
|
|
2217
|
+
if (raw === "off" || raw === "window" || raw === "fullscreen") {
|
|
2218
|
+
return raw;
|
|
2219
|
+
}
|
|
2220
|
+
return defaultScope ?? DEFAULT_OBSERVER_POLICY.scope;
|
|
2221
|
+
}
|
|
2222
|
+
function parseConsent(raw, defaultConsent) {
|
|
2223
|
+
if (raw === "per-capture" || raw === "per-session" || raw === "standing") {
|
|
2224
|
+
return raw;
|
|
2225
|
+
}
|
|
2226
|
+
return defaultConsent ?? DEFAULT_OBSERVER_POLICY.consent;
|
|
2227
|
+
}
|
|
2228
|
+
function parseRetention(raw) {
|
|
2229
|
+
return raw === "none" || raw === "session" || raw === "persist" ? raw : DEFAULT_OBSERVER_POLICY.retention;
|
|
2230
|
+
}
|
|
2231
|
+
function parseBool(raw, fallback) {
|
|
2232
|
+
if (raw === void 0) return fallback;
|
|
2233
|
+
return raw === "true";
|
|
2234
|
+
}
|
|
2235
|
+
function parseObserverPolicy(raw, defaults) {
|
|
2236
|
+
return {
|
|
2237
|
+
version: OBSERVER_POLICY_VERSION,
|
|
2238
|
+
scope: parseScope(raw["observer.scope"], defaults?.scope),
|
|
2239
|
+
allowlist: parseList(raw["observer.allowlist"]),
|
|
2240
|
+
denylist: parseList(raw["observer.denylist"]),
|
|
2241
|
+
consent: parseConsent(raw["observer.consent"], defaults?.consent),
|
|
2242
|
+
retention: parseRetention(raw["observer.retention"]),
|
|
2243
|
+
redactWindowTitles: parseBool(
|
|
2244
|
+
raw["observer.redact_titles"],
|
|
2245
|
+
DEFAULT_OBSERVER_POLICY.redactWindowTitles
|
|
2246
|
+
),
|
|
2247
|
+
audioOptIn: parseBool(
|
|
2248
|
+
raw["observer.audio"],
|
|
2249
|
+
DEFAULT_OBSERVER_POLICY.audioOptIn
|
|
2250
|
+
)
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
function getDefaultsForSymbiosisMode(mode) {
|
|
2254
|
+
if (mode === "autonomy") {
|
|
2255
|
+
return { scope: "fullscreen", consent: "standing" };
|
|
2256
|
+
}
|
|
2257
|
+
return { scope: "window", consent: "per-session" };
|
|
2258
|
+
}
|
|
2259
|
+
async function resolveActiveSymbiosisMode(db) {
|
|
2260
|
+
const userId = await getSetting(db, "user.id") || "default";
|
|
2261
|
+
const activeSession = await db.prepare(
|
|
2262
|
+
"SELECT id FROM sessions WHERE user_id = ? AND completed_at IS NULL ORDER BY started_at DESC LIMIT 1"
|
|
2263
|
+
).get(userId);
|
|
2264
|
+
if (activeSession) {
|
|
2265
|
+
const stepToken = await db.prepare(
|
|
2266
|
+
`SELECT t.symbiosis_mode, t.domain
|
|
2267
|
+
FROM session_steps s
|
|
2268
|
+
JOIN tokens t ON t.id = s.token_id
|
|
2269
|
+
WHERE s.session_id = ?
|
|
2270
|
+
ORDER BY s.created_at DESC, s.id DESC
|
|
2271
|
+
LIMIT 1`
|
|
2272
|
+
).get(activeSession.id);
|
|
2273
|
+
if (stepToken) {
|
|
2274
|
+
if (stepToken.symbiosis_mode) {
|
|
2275
|
+
return stepToken.symbiosis_mode;
|
|
2276
|
+
}
|
|
2277
|
+
if (stepToken.domain) {
|
|
2278
|
+
const competence = await getDomainCompetence(db, userId);
|
|
2279
|
+
const comp = competence.find((c) => c.domain === stepToken.domain);
|
|
2280
|
+
if (comp) {
|
|
2281
|
+
return comp.suggestedMode;
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
const lastReviewToken = await db.prepare(
|
|
2287
|
+
`SELECT t.symbiosis_mode, t.domain
|
|
2288
|
+
FROM review_logs r
|
|
2289
|
+
JOIN tokens t ON t.id = r.token_id
|
|
2290
|
+
WHERE r.user_id = ?
|
|
2291
|
+
ORDER BY r.reviewed_at DESC, r.id DESC
|
|
2292
|
+
LIMIT 1`
|
|
2293
|
+
).get(userId);
|
|
2294
|
+
if (lastReviewToken) {
|
|
2295
|
+
if (lastReviewToken.symbiosis_mode) {
|
|
2296
|
+
return lastReviewToken.symbiosis_mode;
|
|
2297
|
+
}
|
|
2298
|
+
if (lastReviewToken.domain) {
|
|
2299
|
+
const competence = await getDomainCompetence(db, userId);
|
|
2300
|
+
const comp = competence.find((c) => c.domain === lastReviewToken.domain);
|
|
2301
|
+
if (comp) {
|
|
2302
|
+
return comp.suggestedMode;
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
return "shadowing";
|
|
2307
|
+
}
|
|
2308
|
+
async function resolveObserverPolicy(db) {
|
|
2309
|
+
const [scope, allowlist, denylist, consent, retention, redactTitles, audio] = await Promise.all([
|
|
2310
|
+
getSetting(db, "observer.scope"),
|
|
2311
|
+
getSetting(db, "observer.allowlist"),
|
|
2312
|
+
getSetting(db, "observer.denylist"),
|
|
2313
|
+
getSetting(db, "observer.consent"),
|
|
2314
|
+
getSetting(db, "observer.retention"),
|
|
2315
|
+
getSetting(db, "observer.redact_titles"),
|
|
2316
|
+
getSetting(db, "observer.audio")
|
|
2317
|
+
]);
|
|
2318
|
+
const mode = await resolveActiveSymbiosisMode(db);
|
|
2319
|
+
const defaults = getDefaultsForSymbiosisMode(mode);
|
|
2320
|
+
return parseObserverPolicy(
|
|
2321
|
+
{
|
|
2322
|
+
"observer.scope": scope,
|
|
2323
|
+
"observer.allowlist": allowlist,
|
|
2324
|
+
"observer.denylist": denylist,
|
|
2325
|
+
"observer.consent": consent,
|
|
2326
|
+
"observer.retention": retention,
|
|
2327
|
+
"observer.redact_titles": redactTitles,
|
|
2328
|
+
"observer.audio": audio
|
|
2329
|
+
},
|
|
2330
|
+
defaults
|
|
2331
|
+
);
|
|
2332
|
+
}
|
|
2333
|
+
function haystacks(...values) {
|
|
2334
|
+
return values.filter((v) => typeof v === "string" && v.length > 0).map((v) => v.toLowerCase());
|
|
2335
|
+
}
|
|
2336
|
+
function deny(reason, denialReason) {
|
|
2337
|
+
return { allowed: false, reason, denialReason };
|
|
2338
|
+
}
|
|
2339
|
+
function matchBuiltInSensitive(processName, windowTitle) {
|
|
2340
|
+
const fields = haystacks(processName, windowTitle);
|
|
2341
|
+
for (const matcher of BUILT_IN_SENSITIVE_MATCHERS) {
|
|
2342
|
+
if (fields.some((field) => field.includes(matcher))) return matcher;
|
|
2343
|
+
}
|
|
2344
|
+
return null;
|
|
2345
|
+
}
|
|
2346
|
+
function matchDenylist(policy, processName, windowTitle) {
|
|
2347
|
+
const fields = haystacks(processName, windowTitle);
|
|
2348
|
+
for (const matcher of policy.denylist) {
|
|
2349
|
+
if (fields.some((field) => field.includes(matcher))) return matcher;
|
|
2350
|
+
}
|
|
2351
|
+
return null;
|
|
2352
|
+
}
|
|
2353
|
+
function inAllowlist(policy, processName) {
|
|
2354
|
+
if (policy.allowlist.length === 0) return true;
|
|
2355
|
+
if (!processName) return false;
|
|
2356
|
+
const name = processName.toLowerCase();
|
|
2357
|
+
return policy.allowlist.some((entry) => name.includes(entry));
|
|
2358
|
+
}
|
|
2359
|
+
function decidePreCapture(policy, request) {
|
|
2360
|
+
if (policy.scope === "off") {
|
|
2361
|
+
return deny("observer is disabled (observer.scope=off)", "scope-off");
|
|
2362
|
+
}
|
|
2363
|
+
if (policy.scope === "window" && !request.hasExplicitTarget) {
|
|
2364
|
+
return deny(
|
|
2365
|
+
"window scope requires a target: pass --process-name or --hwnd, or set observer.scope=fullscreen",
|
|
2366
|
+
"scope-requires-target"
|
|
2367
|
+
);
|
|
2368
|
+
}
|
|
2369
|
+
const requested = request.requestedProcessName;
|
|
2370
|
+
if (requested) {
|
|
2371
|
+
const sensitive = matchBuiltInSensitive(requested, null);
|
|
2372
|
+
if (sensitive) {
|
|
2373
|
+
return deny(`refusing sensitive surface (${sensitive})`, "sensitive");
|
|
2374
|
+
}
|
|
2375
|
+
const denied = matchDenylist(policy, requested, null);
|
|
2376
|
+
if (denied) {
|
|
2377
|
+
return deny(`process is denylisted (${denied})`, "denylisted");
|
|
2378
|
+
}
|
|
2379
|
+
if (policy.scope === "window" && !inAllowlist(policy, requested)) {
|
|
2380
|
+
return deny(
|
|
2381
|
+
`process not in observer.allowlist (${requested.toLowerCase()})`,
|
|
2382
|
+
"not-allowlisted"
|
|
2383
|
+
);
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
return { allowed: true };
|
|
2387
|
+
}
|
|
2388
|
+
function decidePostCapture(policy, target) {
|
|
2389
|
+
const isFullscreen = target.method.toLowerCase().includes("fullscreen");
|
|
2390
|
+
if (policy.scope === "window" && isFullscreen) {
|
|
2391
|
+
return deny(
|
|
2392
|
+
"window scope but capture fell back to fullscreen (target window not resolved)",
|
|
2393
|
+
"scope-requires-target"
|
|
2394
|
+
);
|
|
2395
|
+
}
|
|
2396
|
+
const sensitive = matchBuiltInSensitive(
|
|
2397
|
+
target.processName,
|
|
2398
|
+
target.windowTitle
|
|
2399
|
+
);
|
|
2400
|
+
if (sensitive) {
|
|
2401
|
+
return deny(`refusing sensitive surface (${sensitive})`, "sensitive");
|
|
2402
|
+
}
|
|
2403
|
+
const denied = matchDenylist(policy, target.processName, target.windowTitle);
|
|
2404
|
+
if (denied) {
|
|
2405
|
+
return deny(`captured window is denylisted (${denied})`, "denylisted");
|
|
2406
|
+
}
|
|
2407
|
+
if (policy.scope === "window" && !inAllowlist(policy, target.processName)) {
|
|
2408
|
+
return deny(
|
|
2409
|
+
`captured window not in observer.allowlist (${target.processName ?? "unknown"})`,
|
|
2410
|
+
"not-allowlisted"
|
|
2411
|
+
);
|
|
2412
|
+
}
|
|
2413
|
+
return { allowed: true };
|
|
2414
|
+
}
|
|
2415
|
+
async function isObserverPolicyConfigured(db) {
|
|
2416
|
+
const settings = await getAllSettings(db);
|
|
2417
|
+
return Object.keys(settings).some((key) => key.startsWith("observer."));
|
|
2418
|
+
}
|
|
2419
|
+
var OBSERVER_POLICY_UNSET_HINT = "Observer policy is at defaults (scope=window: only a targeted window is captured). Configure with `zam observer status|grant|revoke` or `zam settings set observer.scope <off|window|fullscreen>`.";
|
|
2420
|
+
|
|
2421
|
+
// src/kernel/observation/ui-observer-io.ts
|
|
2422
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5 } from "fs";
|
|
2423
|
+
import { homedir as homedir4 } from "os";
|
|
2424
|
+
import { join as join5 } from "path";
|
|
2425
|
+
|
|
2426
|
+
// src/kernel/observation/ui-observer.ts
|
|
2427
|
+
var UI_OBSERVATION_PROTOCOL_VERSION = 1;
|
|
2428
|
+
var OBSERVATION_KINDS = /* @__PURE__ */ new Set([
|
|
2429
|
+
"progress",
|
|
2430
|
+
"step-completed",
|
|
2431
|
+
"error",
|
|
2432
|
+
"help-seeking",
|
|
2433
|
+
"uncertain",
|
|
2434
|
+
"privacy-pause",
|
|
2435
|
+
"heartbeat"
|
|
2436
|
+
]);
|
|
2437
|
+
var ACTION_TYPES = /* @__PURE__ */ new Set([
|
|
2438
|
+
"click",
|
|
2439
|
+
"shortcut",
|
|
2440
|
+
"typing",
|
|
2441
|
+
"scroll",
|
|
2442
|
+
"window-change"
|
|
2443
|
+
]);
|
|
2444
|
+
var EVIDENCE_TYPES = /* @__PURE__ */ new Set([
|
|
2445
|
+
"uia",
|
|
2446
|
+
"keyframe",
|
|
2447
|
+
"clip",
|
|
2448
|
+
"window"
|
|
2449
|
+
]);
|
|
2450
|
+
function isUiObservationReport(value) {
|
|
2451
|
+
if (!isRecord(value)) return false;
|
|
2452
|
+
if (value.version !== UI_OBSERVATION_PROTOCOL_VERSION) return false;
|
|
2453
|
+
if (!isNonEmptyString(value.sessionId)) return false;
|
|
2454
|
+
if (!isNonNegativeInteger(value.sequence)) return false;
|
|
2455
|
+
if (!isNonEmptyString(value.observedFrom)) return false;
|
|
2456
|
+
if (!isNonEmptyString(value.observedTo)) return false;
|
|
2457
|
+
if (typeof value.kind !== "string" || !OBSERVATION_KINDS.has(value.kind)) {
|
|
2458
|
+
return false;
|
|
2459
|
+
}
|
|
2460
|
+
if (!isApplication(value.application)) return false;
|
|
2461
|
+
if (!isNonEmptyString(value.summary)) return false;
|
|
2462
|
+
if (!Array.isArray(value.actions) || !value.actions.every(isObservedAction)) {
|
|
2463
|
+
return false;
|
|
2464
|
+
}
|
|
2465
|
+
if (!Array.isArray(value.evidence) || !value.evidence.every(isEvidenceRef)) {
|
|
2466
|
+
return false;
|
|
2467
|
+
}
|
|
2468
|
+
if (!Array.isArray(value.candidateTokens) || !value.candidateTokens.every(isCandidateToken)) {
|
|
2469
|
+
return false;
|
|
2470
|
+
}
|
|
2471
|
+
return isConfidence(value.confidence);
|
|
2472
|
+
}
|
|
2473
|
+
function parseUiObservationLog(jsonl) {
|
|
2474
|
+
const reports = [];
|
|
2475
|
+
for (const line of jsonl.split("\n")) {
|
|
2476
|
+
const trimmed = line.trim();
|
|
2477
|
+
if (!trimmed) continue;
|
|
2478
|
+
try {
|
|
2479
|
+
const value = JSON.parse(trimmed);
|
|
2480
|
+
if (isUiObservationReport(value)) {
|
|
2481
|
+
reports.push(value);
|
|
2482
|
+
}
|
|
2483
|
+
} catch {
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
return reports.sort((left, right) => left.sequence - right.sequence);
|
|
2487
|
+
}
|
|
2488
|
+
function isApplication(value) {
|
|
2489
|
+
if (!isRecord(value) || !isNonEmptyString(value.processName)) return false;
|
|
2490
|
+
if (value.processId !== void 0 && !isNonNegativeInteger(value.processId)) {
|
|
2491
|
+
return false;
|
|
2492
|
+
}
|
|
2493
|
+
return value.windowTitle === void 0 || typeof value.windowTitle === "string";
|
|
2494
|
+
}
|
|
2495
|
+
function isObservedAction(value) {
|
|
2496
|
+
if (!isRecord(value) || typeof value.type !== "string") return false;
|
|
2497
|
+
if (!ACTION_TYPES.has(value.type)) return false;
|
|
2498
|
+
if (value.target !== void 0 && typeof value.target !== "string") {
|
|
2499
|
+
return false;
|
|
2500
|
+
}
|
|
2501
|
+
return value.result === void 0 || typeof value.result === "string";
|
|
2502
|
+
}
|
|
2503
|
+
function isEvidenceRef(value) {
|
|
2504
|
+
if (!isRecord(value) || typeof value.type !== "string") return false;
|
|
2505
|
+
return EVIDENCE_TYPES.has(value.type) && isNonEmptyString(value.ref) && typeof value.redacted === "boolean";
|
|
2506
|
+
}
|
|
2507
|
+
function isCandidateToken(value) {
|
|
2508
|
+
return isRecord(value) && isNonEmptyString(value.slug) && isConfidence(value.confidence) && isNonEmptyString(value.rationale);
|
|
2509
|
+
}
|
|
2510
|
+
function isRecord(value) {
|
|
2511
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2512
|
+
}
|
|
2513
|
+
function isNonEmptyString(value) {
|
|
2514
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
2515
|
+
}
|
|
2516
|
+
function isNonNegativeInteger(value) {
|
|
2517
|
+
return Number.isInteger(value) && Number(value) >= 0;
|
|
2518
|
+
}
|
|
2519
|
+
function isConfidence(value) {
|
|
2520
|
+
return typeof value === "number" && value >= 0 && value <= 1;
|
|
2521
|
+
}
|
|
2522
|
+
|
|
2523
|
+
// src/kernel/observation/ui-observer-io.ts
|
|
2524
|
+
var DEFAULT_UI_OBSERVER_DIR = join5(homedir4(), ".zam", "observer");
|
|
2525
|
+
function getUiObserverDir() {
|
|
2526
|
+
return process.env.ZAM_OBSERVER_DIR || DEFAULT_UI_OBSERVER_DIR;
|
|
2527
|
+
}
|
|
2528
|
+
function getUiObservationPath(sessionId) {
|
|
2529
|
+
if (!/^[A-Za-z0-9_-]+$/.test(sessionId)) {
|
|
2530
|
+
throw new Error(`Invalid observer session ID: ${sessionId}`);
|
|
2531
|
+
}
|
|
2532
|
+
return join5(getUiObserverDir(), `${sessionId}.reports.jsonl`);
|
|
2533
|
+
}
|
|
2534
|
+
function ensureUiObserverDir() {
|
|
2535
|
+
const dir = getUiObserverDir();
|
|
2536
|
+
if (!existsSync5(dir)) {
|
|
2537
|
+
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
function uiObservationLogExists(sessionId) {
|
|
2541
|
+
return existsSync5(getUiObservationPath(sessionId));
|
|
2542
|
+
}
|
|
2543
|
+
function readUiObservationLog(sessionId) {
|
|
2544
|
+
const path = getUiObservationPath(sessionId);
|
|
2545
|
+
if (!existsSync5(path)) return [];
|
|
2546
|
+
return parseUiObservationLog(readFileSync5(path, "utf8"));
|
|
2547
|
+
}
|
|
2548
|
+
function appendUiObservationReport(report) {
|
|
2549
|
+
if (!isUiObservationReport(report)) {
|
|
2550
|
+
throw new Error("Invalid UI observation report");
|
|
2551
|
+
}
|
|
2552
|
+
ensureUiObserverDir();
|
|
2553
|
+
appendFileSync2(
|
|
2554
|
+
getUiObservationPath(report.sessionId),
|
|
2555
|
+
`${JSON.stringify(report)}
|
|
2556
|
+
`,
|
|
2557
|
+
{ encoding: "utf8", mode: 384 }
|
|
2558
|
+
);
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2561
|
+
// src/kernel/observation/observer-sidecar-policy.ts
|
|
2562
|
+
var SIDECAR_POLICY_FILE = "policy.json";
|
|
2563
|
+
function toSidecarPrivacyPolicy(policy) {
|
|
2564
|
+
return {
|
|
2565
|
+
allowProcesses: [...policy.allowlist],
|
|
2566
|
+
denyProcesses: [...policy.denylist],
|
|
2567
|
+
denyTitleMarkers: [...policy.denylist]
|
|
2568
|
+
};
|
|
2569
|
+
}
|
|
2570
|
+
async function syncObserverSidecarPolicy(db, dir = getUiObserverDir()) {
|
|
2571
|
+
const policy = toSidecarPrivacyPolicy(await resolveObserverPolicy(db));
|
|
2572
|
+
mkdirSync5(dir, { recursive: true, mode: 448 });
|
|
2573
|
+
const path = join6(dir, SIDECAR_POLICY_FILE);
|
|
2574
|
+
writeFileSync3(path, `${JSON.stringify(policy, null, 2)}
|
|
2575
|
+
`, {
|
|
2576
|
+
encoding: "utf8",
|
|
2577
|
+
mode: 384
|
|
2578
|
+
});
|
|
2579
|
+
return { path, policy };
|
|
2580
|
+
}
|
|
2581
|
+
|
|
2124
2582
|
// src/kernel/observation/session-synthesis.ts
|
|
2125
2583
|
import { ulid as ulid7 } from "ulid";
|
|
2126
2584
|
|
|
@@ -2391,146 +2849,6 @@ async function unblockReady(db, userId) {
|
|
|
2391
2849
|
return { unblocked };
|
|
2392
2850
|
}
|
|
2393
2851
|
|
|
2394
|
-
// src/kernel/observation/ui-observer-io.ts
|
|
2395
|
-
import { appendFileSync as appendFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5 } from "fs";
|
|
2396
|
-
import { homedir as homedir4 } from "os";
|
|
2397
|
-
import { join as join5 } from "path";
|
|
2398
|
-
|
|
2399
|
-
// src/kernel/observation/ui-observer.ts
|
|
2400
|
-
var UI_OBSERVATION_PROTOCOL_VERSION = 1;
|
|
2401
|
-
var OBSERVATION_KINDS = /* @__PURE__ */ new Set([
|
|
2402
|
-
"progress",
|
|
2403
|
-
"step-completed",
|
|
2404
|
-
"error",
|
|
2405
|
-
"help-seeking",
|
|
2406
|
-
"uncertain",
|
|
2407
|
-
"privacy-pause",
|
|
2408
|
-
"heartbeat"
|
|
2409
|
-
]);
|
|
2410
|
-
var ACTION_TYPES = /* @__PURE__ */ new Set([
|
|
2411
|
-
"click",
|
|
2412
|
-
"shortcut",
|
|
2413
|
-
"typing",
|
|
2414
|
-
"scroll",
|
|
2415
|
-
"window-change"
|
|
2416
|
-
]);
|
|
2417
|
-
var EVIDENCE_TYPES = /* @__PURE__ */ new Set([
|
|
2418
|
-
"uia",
|
|
2419
|
-
"keyframe",
|
|
2420
|
-
"clip",
|
|
2421
|
-
"window"
|
|
2422
|
-
]);
|
|
2423
|
-
function isUiObservationReport(value) {
|
|
2424
|
-
if (!isRecord(value)) return false;
|
|
2425
|
-
if (value.version !== UI_OBSERVATION_PROTOCOL_VERSION) return false;
|
|
2426
|
-
if (!isNonEmptyString(value.sessionId)) return false;
|
|
2427
|
-
if (!isNonNegativeInteger(value.sequence)) return false;
|
|
2428
|
-
if (!isNonEmptyString(value.observedFrom)) return false;
|
|
2429
|
-
if (!isNonEmptyString(value.observedTo)) return false;
|
|
2430
|
-
if (typeof value.kind !== "string" || !OBSERVATION_KINDS.has(value.kind)) {
|
|
2431
|
-
return false;
|
|
2432
|
-
}
|
|
2433
|
-
if (!isApplication(value.application)) return false;
|
|
2434
|
-
if (!isNonEmptyString(value.summary)) return false;
|
|
2435
|
-
if (!Array.isArray(value.actions) || !value.actions.every(isObservedAction)) {
|
|
2436
|
-
return false;
|
|
2437
|
-
}
|
|
2438
|
-
if (!Array.isArray(value.evidence) || !value.evidence.every(isEvidenceRef)) {
|
|
2439
|
-
return false;
|
|
2440
|
-
}
|
|
2441
|
-
if (!Array.isArray(value.candidateTokens) || !value.candidateTokens.every(isCandidateToken)) {
|
|
2442
|
-
return false;
|
|
2443
|
-
}
|
|
2444
|
-
return isConfidence(value.confidence);
|
|
2445
|
-
}
|
|
2446
|
-
function parseUiObservationLog(jsonl) {
|
|
2447
|
-
const reports = [];
|
|
2448
|
-
for (const line of jsonl.split("\n")) {
|
|
2449
|
-
const trimmed = line.trim();
|
|
2450
|
-
if (!trimmed) continue;
|
|
2451
|
-
try {
|
|
2452
|
-
const value = JSON.parse(trimmed);
|
|
2453
|
-
if (isUiObservationReport(value)) {
|
|
2454
|
-
reports.push(value);
|
|
2455
|
-
}
|
|
2456
|
-
} catch {
|
|
2457
|
-
}
|
|
2458
|
-
}
|
|
2459
|
-
return reports.sort((left, right) => left.sequence - right.sequence);
|
|
2460
|
-
}
|
|
2461
|
-
function isApplication(value) {
|
|
2462
|
-
if (!isRecord(value) || !isNonEmptyString(value.processName)) return false;
|
|
2463
|
-
if (value.processId !== void 0 && !isNonNegativeInteger(value.processId)) {
|
|
2464
|
-
return false;
|
|
2465
|
-
}
|
|
2466
|
-
return value.windowTitle === void 0 || typeof value.windowTitle === "string";
|
|
2467
|
-
}
|
|
2468
|
-
function isObservedAction(value) {
|
|
2469
|
-
if (!isRecord(value) || typeof value.type !== "string") return false;
|
|
2470
|
-
if (!ACTION_TYPES.has(value.type)) return false;
|
|
2471
|
-
if (value.target !== void 0 && typeof value.target !== "string") {
|
|
2472
|
-
return false;
|
|
2473
|
-
}
|
|
2474
|
-
return value.result === void 0 || typeof value.result === "string";
|
|
2475
|
-
}
|
|
2476
|
-
function isEvidenceRef(value) {
|
|
2477
|
-
if (!isRecord(value) || typeof value.type !== "string") return false;
|
|
2478
|
-
return EVIDENCE_TYPES.has(value.type) && isNonEmptyString(value.ref) && typeof value.redacted === "boolean";
|
|
2479
|
-
}
|
|
2480
|
-
function isCandidateToken(value) {
|
|
2481
|
-
return isRecord(value) && isNonEmptyString(value.slug) && isConfidence(value.confidence) && isNonEmptyString(value.rationale);
|
|
2482
|
-
}
|
|
2483
|
-
function isRecord(value) {
|
|
2484
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2485
|
-
}
|
|
2486
|
-
function isNonEmptyString(value) {
|
|
2487
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
2488
|
-
}
|
|
2489
|
-
function isNonNegativeInteger(value) {
|
|
2490
|
-
return Number.isInteger(value) && Number(value) >= 0;
|
|
2491
|
-
}
|
|
2492
|
-
function isConfidence(value) {
|
|
2493
|
-
return typeof value === "number" && value >= 0 && value <= 1;
|
|
2494
|
-
}
|
|
2495
|
-
|
|
2496
|
-
// src/kernel/observation/ui-observer-io.ts
|
|
2497
|
-
var DEFAULT_UI_OBSERVER_DIR = join5(homedir4(), ".zam", "observer");
|
|
2498
|
-
function getUiObserverDir() {
|
|
2499
|
-
return process.env.ZAM_OBSERVER_DIR || DEFAULT_UI_OBSERVER_DIR;
|
|
2500
|
-
}
|
|
2501
|
-
function getUiObservationPath(sessionId) {
|
|
2502
|
-
if (!/^[A-Za-z0-9_-]+$/.test(sessionId)) {
|
|
2503
|
-
throw new Error(`Invalid observer session ID: ${sessionId}`);
|
|
2504
|
-
}
|
|
2505
|
-
return join5(getUiObserverDir(), `${sessionId}.reports.jsonl`);
|
|
2506
|
-
}
|
|
2507
|
-
function ensureUiObserverDir() {
|
|
2508
|
-
const dir = getUiObserverDir();
|
|
2509
|
-
if (!existsSync5(dir)) {
|
|
2510
|
-
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
2511
|
-
}
|
|
2512
|
-
}
|
|
2513
|
-
function uiObservationLogExists(sessionId) {
|
|
2514
|
-
return existsSync5(getUiObservationPath(sessionId));
|
|
2515
|
-
}
|
|
2516
|
-
function readUiObservationLog(sessionId) {
|
|
2517
|
-
const path = getUiObservationPath(sessionId);
|
|
2518
|
-
if (!existsSync5(path)) return [];
|
|
2519
|
-
return parseUiObservationLog(readFileSync5(path, "utf8"));
|
|
2520
|
-
}
|
|
2521
|
-
function appendUiObservationReport(report) {
|
|
2522
|
-
if (!isUiObservationReport(report)) {
|
|
2523
|
-
throw new Error("Invalid UI observation report");
|
|
2524
|
-
}
|
|
2525
|
-
ensureUiObserverDir();
|
|
2526
|
-
appendFileSync2(
|
|
2527
|
-
getUiObservationPath(report.sessionId),
|
|
2528
|
-
`${JSON.stringify(report)}
|
|
2529
|
-
`,
|
|
2530
|
-
{ encoding: "utf8", mode: 384 }
|
|
2531
|
-
);
|
|
2532
|
-
}
|
|
2533
|
-
|
|
2534
2852
|
// src/kernel/observation/ui-observer-synthesis.ts
|
|
2535
2853
|
var MIN_UI_CONFIDENCE = 0.6;
|
|
2536
2854
|
function kindToRating(kind) {
|
|
@@ -3335,7 +3653,7 @@ function generatePrompt(input) {
|
|
|
3335
3653
|
|
|
3336
3654
|
// src/kernel/recall/reference-resolver.ts
|
|
3337
3655
|
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
3338
|
-
import { dirname as dirname3, join as
|
|
3656
|
+
import { dirname as dirname3, join as join7, resolve } from "path";
|
|
3339
3657
|
var DEFAULT_REVIEW_CONTEXT_MAX_CHARS = 6e3;
|
|
3340
3658
|
function htmlToText(html) {
|
|
3341
3659
|
let content = html;
|
|
@@ -3396,8 +3714,8 @@ async function resolveReference(sourceLink) {
|
|
|
3396
3714
|
const filePath = anchorIndex2 !== -1 ? fullPathWithAnchor.slice(0, anchorIndex2) : fullPathWithAnchor;
|
|
3397
3715
|
const anchor2 = anchorIndex2 !== -1 ? fullPathWithAnchor.slice(anchorIndex2) : "";
|
|
3398
3716
|
const parentDir = dirname3(process.cwd());
|
|
3399
|
-
const localRepoPath =
|
|
3400
|
-
const localFilePath =
|
|
3717
|
+
const localRepoPath = join7(parentDir, repo);
|
|
3718
|
+
const localFilePath = join7(localRepoPath, filePath);
|
|
3401
3719
|
if (existsSync6(localFilePath)) {
|
|
3402
3720
|
try {
|
|
3403
3721
|
let fileContent = readFileSync6(localFilePath, "utf-8");
|
|
@@ -3711,12 +4029,12 @@ import {
|
|
|
3711
4029
|
appendFileSync as appendFileSync3,
|
|
3712
4030
|
copyFileSync,
|
|
3713
4031
|
existsSync as existsSync7,
|
|
3714
|
-
mkdirSync as
|
|
4032
|
+
mkdirSync as mkdirSync6,
|
|
3715
4033
|
readFileSync as readFileSync7,
|
|
3716
|
-
writeFileSync as
|
|
4034
|
+
writeFileSync as writeFileSync4
|
|
3717
4035
|
} from "fs";
|
|
3718
4036
|
import { homedir as homedir5 } from "os";
|
|
3719
|
-
import { join as
|
|
4037
|
+
import { join as join8 } from "path";
|
|
3720
4038
|
import { fileURLToPath } from "url";
|
|
3721
4039
|
var HOME = homedir5();
|
|
3722
4040
|
function getPackageSkillPath(agent = "default") {
|
|
@@ -3724,15 +4042,15 @@ function getPackageSkillPath(agent = "default") {
|
|
|
3724
4042
|
fileURLToPath(new URL("../../..", import.meta.url)),
|
|
3725
4043
|
fileURLToPath(new URL("../..", import.meta.url)),
|
|
3726
4044
|
fileURLToPath(new URL("..", import.meta.url))
|
|
3727
|
-
].find((candidate) => existsSync7(
|
|
4045
|
+
].find((candidate) => existsSync7(join8(candidate, "package.json"))) ?? "";
|
|
3728
4046
|
if (!packageRoot) return "";
|
|
3729
4047
|
if (agent === "codex") {
|
|
3730
|
-
const codexPath =
|
|
4048
|
+
const codexPath = join8(packageRoot, ".agents", "skills", "zam", "SKILL.md");
|
|
3731
4049
|
if (existsSync7(codexPath)) return codexPath;
|
|
3732
4050
|
return "";
|
|
3733
4051
|
}
|
|
3734
4052
|
if (agent === "claude") {
|
|
3735
|
-
const claudePath =
|
|
4053
|
+
const claudePath = join8(
|
|
3736
4054
|
packageRoot,
|
|
3737
4055
|
".claude",
|
|
3738
4056
|
"skills",
|
|
@@ -3742,9 +4060,9 @@ function getPackageSkillPath(agent = "default") {
|
|
|
3742
4060
|
if (existsSync7(claudePath)) return claudePath;
|
|
3743
4061
|
return "";
|
|
3744
4062
|
}
|
|
3745
|
-
let path =
|
|
4063
|
+
let path = join8(packageRoot, ".agent", "skills", "zam", "SKILL.md");
|
|
3746
4064
|
if (existsSync7(path)) return path;
|
|
3747
|
-
path =
|
|
4065
|
+
path = join8(packageRoot, ".claude", "skills", "zam", "SKILL.md");
|
|
3748
4066
|
if (existsSync7(path)) return path;
|
|
3749
4067
|
return "";
|
|
3750
4068
|
}
|
|
@@ -3757,16 +4075,16 @@ function distributeGlobalSkills(home = HOME) {
|
|
|
3757
4075
|
console.warn("Could not find ZAM source SKILL.md in the package folder.");
|
|
3758
4076
|
return results;
|
|
3759
4077
|
}
|
|
3760
|
-
const claudeSkillsDir =
|
|
4078
|
+
const claudeSkillsDir = join8(home, ".claude", "skills", "zam");
|
|
3761
4079
|
try {
|
|
3762
4080
|
if (!claudeSourceSkill) {
|
|
3763
4081
|
throw new Error("Claude skill source not found");
|
|
3764
4082
|
}
|
|
3765
|
-
|
|
3766
|
-
copyFileSync(claudeSourceSkill,
|
|
4083
|
+
mkdirSync6(claudeSkillsDir, { recursive: true });
|
|
4084
|
+
copyFileSync(claudeSourceSkill, join8(claudeSkillsDir, "SKILL.md"));
|
|
3767
4085
|
results.push({
|
|
3768
4086
|
name: "Claude Code Global",
|
|
3769
|
-
path:
|
|
4087
|
+
path: join8(claudeSkillsDir, "SKILL.md"),
|
|
3770
4088
|
success: true
|
|
3771
4089
|
});
|
|
3772
4090
|
} catch (_err) {
|
|
@@ -3776,13 +4094,13 @@ function distributeGlobalSkills(home = HOME) {
|
|
|
3776
4094
|
success: false
|
|
3777
4095
|
});
|
|
3778
4096
|
}
|
|
3779
|
-
const geminiSkillsDir =
|
|
4097
|
+
const geminiSkillsDir = join8(home, ".gemini", "skills", "zam");
|
|
3780
4098
|
try {
|
|
3781
|
-
|
|
3782
|
-
copyFileSync(sourceSkill,
|
|
4099
|
+
mkdirSync6(geminiSkillsDir, { recursive: true });
|
|
4100
|
+
copyFileSync(sourceSkill, join8(geminiSkillsDir, "SKILL.md"));
|
|
3783
4101
|
results.push({
|
|
3784
4102
|
name: "Gemini CLI Global",
|
|
3785
|
-
path:
|
|
4103
|
+
path: join8(geminiSkillsDir, "SKILL.md"),
|
|
3786
4104
|
success: true
|
|
3787
4105
|
});
|
|
3788
4106
|
} catch (_err) {
|
|
@@ -3792,16 +4110,16 @@ function distributeGlobalSkills(home = HOME) {
|
|
|
3792
4110
|
success: false
|
|
3793
4111
|
});
|
|
3794
4112
|
}
|
|
3795
|
-
const codexSkillsDir =
|
|
4113
|
+
const codexSkillsDir = join8(home, ".agents", "skills", "zam");
|
|
3796
4114
|
try {
|
|
3797
4115
|
if (!codexSourceSkill) {
|
|
3798
4116
|
throw new Error("Codex skill source not found");
|
|
3799
4117
|
}
|
|
3800
|
-
|
|
3801
|
-
copyFileSync(codexSourceSkill,
|
|
4118
|
+
mkdirSync6(codexSkillsDir, { recursive: true });
|
|
4119
|
+
copyFileSync(codexSourceSkill, join8(codexSkillsDir, "SKILL.md"));
|
|
3802
4120
|
results.push({
|
|
3803
4121
|
name: "Codex Global",
|
|
3804
|
-
path:
|
|
4122
|
+
path: join8(codexSkillsDir, "SKILL.md"),
|
|
3805
4123
|
success: true
|
|
3806
4124
|
});
|
|
3807
4125
|
} catch (_err) {
|
|
@@ -3811,13 +4129,13 @@ function distributeGlobalSkills(home = HOME) {
|
|
|
3811
4129
|
success: false
|
|
3812
4130
|
});
|
|
3813
4131
|
}
|
|
3814
|
-
const gooseSkillsDir =
|
|
4132
|
+
const gooseSkillsDir = join8(home, ".goose", "skills", "zam");
|
|
3815
4133
|
try {
|
|
3816
|
-
|
|
3817
|
-
copyFileSync(sourceSkill,
|
|
4134
|
+
mkdirSync6(gooseSkillsDir, { recursive: true });
|
|
4135
|
+
copyFileSync(sourceSkill, join8(gooseSkillsDir, "SKILL.md"));
|
|
3818
4136
|
results.push({
|
|
3819
4137
|
name: "Goose Global",
|
|
3820
|
-
path:
|
|
4138
|
+
path: join8(gooseSkillsDir, "SKILL.md"),
|
|
3821
4139
|
success: true
|
|
3822
4140
|
});
|
|
3823
4141
|
} catch (_err) {
|
|
@@ -3866,7 +4184,7 @@ function installHook(file, hook, oldHook) {
|
|
|
3866
4184
|
return { success: true, alreadyHooked: true };
|
|
3867
4185
|
}
|
|
3868
4186
|
if (content.includes(oldHook.trim())) {
|
|
3869
|
-
|
|
4187
|
+
writeFileSync4(file, content.replace(oldHook.trim(), hook.trim()), "utf8");
|
|
3870
4188
|
} else {
|
|
3871
4189
|
appendFileSync3(file, hook);
|
|
3872
4190
|
}
|
|
@@ -3877,24 +4195,24 @@ function installHook(file, hook, oldHook) {
|
|
|
3877
4195
|
}
|
|
3878
4196
|
function injectShellHooks(home = HOME) {
|
|
3879
4197
|
const results = [];
|
|
3880
|
-
const zshrc =
|
|
4198
|
+
const zshrc = join8(home, ".zshrc");
|
|
3881
4199
|
if (existsSync7(zshrc)) {
|
|
3882
4200
|
const status = installHook(zshrc, posixHook("zsh"), POSIX_OLD_HOOK);
|
|
3883
4201
|
results.push({ shell: "zsh", file: zshrc, ...status });
|
|
3884
4202
|
}
|
|
3885
|
-
const bashrc =
|
|
4203
|
+
const bashrc = join8(home, ".bashrc");
|
|
3886
4204
|
if (existsSync7(bashrc)) {
|
|
3887
4205
|
const status = installHook(bashrc, posixHook("bash"), POSIX_OLD_HOOK);
|
|
3888
4206
|
results.push({ shell: "bash", file: bashrc, ...status });
|
|
3889
4207
|
}
|
|
3890
4208
|
const pwshDirs = [
|
|
3891
|
-
|
|
3892
|
-
|
|
4209
|
+
join8(home, "Documents", "PowerShell"),
|
|
4210
|
+
join8(home, "Documents", "WindowsPowerShell")
|
|
3893
4211
|
];
|
|
3894
4212
|
for (const dir of pwshDirs) {
|
|
3895
|
-
const profileFile =
|
|
4213
|
+
const profileFile = join8(dir, "Microsoft.PowerShell_profile.ps1");
|
|
3896
4214
|
try {
|
|
3897
|
-
|
|
4215
|
+
mkdirSync6(dir, { recursive: true });
|
|
3898
4216
|
const status = installHook(
|
|
3899
4217
|
profileFile,
|
|
3900
4218
|
POWERSHELL_HOOK,
|
|
@@ -4126,10 +4444,10 @@ function t(locale, key, params = {}) {
|
|
|
4126
4444
|
}
|
|
4127
4445
|
|
|
4128
4446
|
// src/kernel/system/install-config.ts
|
|
4129
|
-
import { existsSync as existsSync8, mkdirSync as
|
|
4447
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
4130
4448
|
import { homedir as homedir6 } from "os";
|
|
4131
|
-
import { dirname as dirname4, join as
|
|
4132
|
-
var DEFAULT_CONFIG_PATH =
|
|
4449
|
+
import { dirname as dirname4, join as join9 } from "path";
|
|
4450
|
+
var DEFAULT_CONFIG_PATH = join9(homedir6(), ".zam", "config.json");
|
|
4133
4451
|
function loadInstallConfig(path = DEFAULT_CONFIG_PATH) {
|
|
4134
4452
|
if (!existsSync8(path)) return {};
|
|
4135
4453
|
try {
|
|
@@ -4140,8 +4458,8 @@ function loadInstallConfig(path = DEFAULT_CONFIG_PATH) {
|
|
|
4140
4458
|
}
|
|
4141
4459
|
function saveInstallConfig(config, path = DEFAULT_CONFIG_PATH) {
|
|
4142
4460
|
const dir = dirname4(path);
|
|
4143
|
-
if (!existsSync8(dir))
|
|
4144
|
-
|
|
4461
|
+
if (!existsSync8(dir)) mkdirSync7(dir, { recursive: true });
|
|
4462
|
+
writeFileSync5(path, `${JSON.stringify(config, null, 2)}
|
|
4145
4463
|
`, "utf-8");
|
|
4146
4464
|
}
|
|
4147
4465
|
function getInstallMode(path = DEFAULT_CONFIG_PATH) {
|
|
@@ -4179,7 +4497,7 @@ function detectSyncProvider(dir) {
|
|
|
4179
4497
|
import { execFileSync, execSync } from "child_process";
|
|
4180
4498
|
import { existsSync as existsSync9 } from "fs";
|
|
4181
4499
|
import { homedir as homedir7 } from "os";
|
|
4182
|
-
import { join as
|
|
4500
|
+
import { join as join10 } from "path";
|
|
4183
4501
|
function hasCommand(cmd) {
|
|
4184
4502
|
try {
|
|
4185
4503
|
const checkCmd = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
|
|
@@ -4224,7 +4542,7 @@ function installOllama() {
|
|
|
4224
4542
|
const isMac = process.platform === "darwin";
|
|
4225
4543
|
const isWin = process.platform === "win32";
|
|
4226
4544
|
const hasOllama = hasCommand("ollama") || isMac && existsSync9("/Applications/Ollama.app") || isWin && existsSync9(
|
|
4227
|
-
|
|
4545
|
+
join10(homedir7(), "AppData", "Local", "Programs", "Ollama", "ollama.exe")
|
|
4228
4546
|
);
|
|
4229
4547
|
if (hasOllama) {
|
|
4230
4548
|
return { success: true, message: "Ollama is already installed." };
|
|
@@ -4284,7 +4602,7 @@ function installOllama() {
|
|
|
4284
4602
|
function resolveOllamaCommand() {
|
|
4285
4603
|
if (hasCommand("ollama")) return "ollama";
|
|
4286
4604
|
const candidates = process.platform === "win32" ? [
|
|
4287
|
-
|
|
4605
|
+
join10(
|
|
4288
4606
|
homedir7(),
|
|
4289
4607
|
"AppData",
|
|
4290
4608
|
"Local",
|
|
@@ -4617,8 +4935,13 @@ function decideUpdate(input) {
|
|
|
4617
4935
|
}
|
|
4618
4936
|
}
|
|
4619
4937
|
export {
|
|
4938
|
+
BUILT_IN_SENSITIVE_MATCHERS,
|
|
4939
|
+
DEFAULT_OBSERVER_POLICY,
|
|
4620
4940
|
DEFAULT_REVIEW_CONTEXT_MAX_CHARS,
|
|
4621
4941
|
HOMEBREW_CASK,
|
|
4942
|
+
OBSERVER_POLICY_UNSET_HINT,
|
|
4943
|
+
OBSERVER_POLICY_VERSION,
|
|
4944
|
+
SIDECAR_POLICY_FILE,
|
|
4622
4945
|
SNAPSHOT_VERSION,
|
|
4623
4946
|
UI_OBSERVATION_PROTOCOL_VERSION,
|
|
4624
4947
|
WINGET_PACKAGE_ID,
|
|
@@ -4636,6 +4959,8 @@ export {
|
|
|
4636
4959
|
createFSRS,
|
|
4637
4960
|
createGoal,
|
|
4638
4961
|
createToken,
|
|
4962
|
+
decidePostCapture,
|
|
4963
|
+
decidePreCapture,
|
|
4639
4964
|
decideUpdate,
|
|
4640
4965
|
deleteCardForUser,
|
|
4641
4966
|
deleteSetting,
|
|
@@ -4672,6 +4997,7 @@ export {
|
|
|
4672
4997
|
getCard,
|
|
4673
4998
|
getCardById,
|
|
4674
4999
|
getCardDeletionImpact,
|
|
5000
|
+
getDatabaseTargetInfo,
|
|
4675
5001
|
getDefaultDbPath,
|
|
4676
5002
|
getDependents,
|
|
4677
5003
|
getDomainCompetence,
|
|
@@ -4707,6 +5033,7 @@ export {
|
|
|
4707
5033
|
installOllama,
|
|
4708
5034
|
installOpenCode,
|
|
4709
5035
|
interleave,
|
|
5036
|
+
isObserverPolicyConfigured,
|
|
4710
5037
|
isUiObservationReport,
|
|
4711
5038
|
listAgentSkills,
|
|
4712
5039
|
listGoals,
|
|
@@ -4716,6 +5043,8 @@ export {
|
|
|
4716
5043
|
loadInstallConfig,
|
|
4717
5044
|
logReview,
|
|
4718
5045
|
logStep,
|
|
5046
|
+
matchBuiltInSensitive,
|
|
5047
|
+
matchDenylist,
|
|
4719
5048
|
matchesFilePath,
|
|
4720
5049
|
monitorLogExists,
|
|
4721
5050
|
normalizeLocale,
|
|
@@ -4726,6 +5055,8 @@ export {
|
|
|
4726
5055
|
pairCommands,
|
|
4727
5056
|
parseGoalFile,
|
|
4728
5057
|
parseMonitorLog,
|
|
5058
|
+
parseObserverList,
|
|
5059
|
+
parseObserverPolicy,
|
|
4729
5060
|
parseSnapshot,
|
|
4730
5061
|
parseUiObservationLog,
|
|
4731
5062
|
planOpenCodeInstall,
|
|
@@ -4735,6 +5066,7 @@ export {
|
|
|
4735
5066
|
readUiObservationLog,
|
|
4736
5067
|
resolveAllBeliefPaths,
|
|
4737
5068
|
resolveAllGoalPaths,
|
|
5069
|
+
resolveObserverPolicy,
|
|
4738
5070
|
resolveReference,
|
|
4739
5071
|
resolveRepoPath,
|
|
4740
5072
|
resolveReviewContext,
|
|
@@ -4747,7 +5079,9 @@ export {
|
|
|
4747
5079
|
setSetting,
|
|
4748
5080
|
setTursoCredentials,
|
|
4749
5081
|
startSession,
|
|
5082
|
+
syncObserverSidecarPolicy,
|
|
4750
5083
|
t,
|
|
5084
|
+
toSidecarPrivacyPolicy,
|
|
4751
5085
|
uiObservationLogExists,
|
|
4752
5086
|
uiObservationTimeSpan,
|
|
4753
5087
|
unblockReady,
|