zam-core 0.3.13 → 0.4.1
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/README.de.md +19 -0
- package/README.md +19 -0
- package/dist/cli/index.js +1538 -469
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +161 -1
- package/dist/index.js +560 -203
- 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;
|
|
@@ -2130,6 +2170,415 @@ function getMonitorLogStats(sessionId) {
|
|
|
2130
2170
|
return { exists: true, sizeBytes: stat.size, lineCount };
|
|
2131
2171
|
}
|
|
2132
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
|
+
|
|
2133
2582
|
// src/kernel/observation/session-synthesis.ts
|
|
2134
2583
|
import { ulid as ulid7 } from "ulid";
|
|
2135
2584
|
|
|
@@ -2400,146 +2849,6 @@ async function unblockReady(db, userId) {
|
|
|
2400
2849
|
return { unblocked };
|
|
2401
2850
|
}
|
|
2402
2851
|
|
|
2403
|
-
// src/kernel/observation/ui-observer-io.ts
|
|
2404
|
-
import { appendFileSync as appendFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync5 } from "fs";
|
|
2405
|
-
import { homedir as homedir4 } from "os";
|
|
2406
|
-
import { join as join5 } from "path";
|
|
2407
|
-
|
|
2408
|
-
// src/kernel/observation/ui-observer.ts
|
|
2409
|
-
var UI_OBSERVATION_PROTOCOL_VERSION = 1;
|
|
2410
|
-
var OBSERVATION_KINDS = /* @__PURE__ */ new Set([
|
|
2411
|
-
"progress",
|
|
2412
|
-
"step-completed",
|
|
2413
|
-
"error",
|
|
2414
|
-
"help-seeking",
|
|
2415
|
-
"uncertain",
|
|
2416
|
-
"privacy-pause",
|
|
2417
|
-
"heartbeat"
|
|
2418
|
-
]);
|
|
2419
|
-
var ACTION_TYPES = /* @__PURE__ */ new Set([
|
|
2420
|
-
"click",
|
|
2421
|
-
"shortcut",
|
|
2422
|
-
"typing",
|
|
2423
|
-
"scroll",
|
|
2424
|
-
"window-change"
|
|
2425
|
-
]);
|
|
2426
|
-
var EVIDENCE_TYPES = /* @__PURE__ */ new Set([
|
|
2427
|
-
"uia",
|
|
2428
|
-
"keyframe",
|
|
2429
|
-
"clip",
|
|
2430
|
-
"window"
|
|
2431
|
-
]);
|
|
2432
|
-
function isUiObservationReport(value) {
|
|
2433
|
-
if (!isRecord(value)) return false;
|
|
2434
|
-
if (value.version !== UI_OBSERVATION_PROTOCOL_VERSION) return false;
|
|
2435
|
-
if (!isNonEmptyString(value.sessionId)) return false;
|
|
2436
|
-
if (!isNonNegativeInteger(value.sequence)) return false;
|
|
2437
|
-
if (!isNonEmptyString(value.observedFrom)) return false;
|
|
2438
|
-
if (!isNonEmptyString(value.observedTo)) return false;
|
|
2439
|
-
if (typeof value.kind !== "string" || !OBSERVATION_KINDS.has(value.kind)) {
|
|
2440
|
-
return false;
|
|
2441
|
-
}
|
|
2442
|
-
if (!isApplication(value.application)) return false;
|
|
2443
|
-
if (!isNonEmptyString(value.summary)) return false;
|
|
2444
|
-
if (!Array.isArray(value.actions) || !value.actions.every(isObservedAction)) {
|
|
2445
|
-
return false;
|
|
2446
|
-
}
|
|
2447
|
-
if (!Array.isArray(value.evidence) || !value.evidence.every(isEvidenceRef)) {
|
|
2448
|
-
return false;
|
|
2449
|
-
}
|
|
2450
|
-
if (!Array.isArray(value.candidateTokens) || !value.candidateTokens.every(isCandidateToken)) {
|
|
2451
|
-
return false;
|
|
2452
|
-
}
|
|
2453
|
-
return isConfidence(value.confidence);
|
|
2454
|
-
}
|
|
2455
|
-
function parseUiObservationLog(jsonl) {
|
|
2456
|
-
const reports = [];
|
|
2457
|
-
for (const line of jsonl.split("\n")) {
|
|
2458
|
-
const trimmed = line.trim();
|
|
2459
|
-
if (!trimmed) continue;
|
|
2460
|
-
try {
|
|
2461
|
-
const value = JSON.parse(trimmed);
|
|
2462
|
-
if (isUiObservationReport(value)) {
|
|
2463
|
-
reports.push(value);
|
|
2464
|
-
}
|
|
2465
|
-
} catch {
|
|
2466
|
-
}
|
|
2467
|
-
}
|
|
2468
|
-
return reports.sort((left, right) => left.sequence - right.sequence);
|
|
2469
|
-
}
|
|
2470
|
-
function isApplication(value) {
|
|
2471
|
-
if (!isRecord(value) || !isNonEmptyString(value.processName)) return false;
|
|
2472
|
-
if (value.processId !== void 0 && !isNonNegativeInteger(value.processId)) {
|
|
2473
|
-
return false;
|
|
2474
|
-
}
|
|
2475
|
-
return value.windowTitle === void 0 || typeof value.windowTitle === "string";
|
|
2476
|
-
}
|
|
2477
|
-
function isObservedAction(value) {
|
|
2478
|
-
if (!isRecord(value) || typeof value.type !== "string") return false;
|
|
2479
|
-
if (!ACTION_TYPES.has(value.type)) return false;
|
|
2480
|
-
if (value.target !== void 0 && typeof value.target !== "string") {
|
|
2481
|
-
return false;
|
|
2482
|
-
}
|
|
2483
|
-
return value.result === void 0 || typeof value.result === "string";
|
|
2484
|
-
}
|
|
2485
|
-
function isEvidenceRef(value) {
|
|
2486
|
-
if (!isRecord(value) || typeof value.type !== "string") return false;
|
|
2487
|
-
return EVIDENCE_TYPES.has(value.type) && isNonEmptyString(value.ref) && typeof value.redacted === "boolean";
|
|
2488
|
-
}
|
|
2489
|
-
function isCandidateToken(value) {
|
|
2490
|
-
return isRecord(value) && isNonEmptyString(value.slug) && isConfidence(value.confidence) && isNonEmptyString(value.rationale);
|
|
2491
|
-
}
|
|
2492
|
-
function isRecord(value) {
|
|
2493
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2494
|
-
}
|
|
2495
|
-
function isNonEmptyString(value) {
|
|
2496
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
2497
|
-
}
|
|
2498
|
-
function isNonNegativeInteger(value) {
|
|
2499
|
-
return Number.isInteger(value) && Number(value) >= 0;
|
|
2500
|
-
}
|
|
2501
|
-
function isConfidence(value) {
|
|
2502
|
-
return typeof value === "number" && value >= 0 && value <= 1;
|
|
2503
|
-
}
|
|
2504
|
-
|
|
2505
|
-
// src/kernel/observation/ui-observer-io.ts
|
|
2506
|
-
var DEFAULT_UI_OBSERVER_DIR = join5(homedir4(), ".zam", "observer");
|
|
2507
|
-
function getUiObserverDir() {
|
|
2508
|
-
return process.env.ZAM_OBSERVER_DIR || DEFAULT_UI_OBSERVER_DIR;
|
|
2509
|
-
}
|
|
2510
|
-
function getUiObservationPath(sessionId) {
|
|
2511
|
-
if (!/^[A-Za-z0-9_-]+$/.test(sessionId)) {
|
|
2512
|
-
throw new Error(`Invalid observer session ID: ${sessionId}`);
|
|
2513
|
-
}
|
|
2514
|
-
return join5(getUiObserverDir(), `${sessionId}.reports.jsonl`);
|
|
2515
|
-
}
|
|
2516
|
-
function ensureUiObserverDir() {
|
|
2517
|
-
const dir = getUiObserverDir();
|
|
2518
|
-
if (!existsSync5(dir)) {
|
|
2519
|
-
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
2520
|
-
}
|
|
2521
|
-
}
|
|
2522
|
-
function uiObservationLogExists(sessionId) {
|
|
2523
|
-
return existsSync5(getUiObservationPath(sessionId));
|
|
2524
|
-
}
|
|
2525
|
-
function readUiObservationLog(sessionId) {
|
|
2526
|
-
const path = getUiObservationPath(sessionId);
|
|
2527
|
-
if (!existsSync5(path)) return [];
|
|
2528
|
-
return parseUiObservationLog(readFileSync5(path, "utf8"));
|
|
2529
|
-
}
|
|
2530
|
-
function appendUiObservationReport(report) {
|
|
2531
|
-
if (!isUiObservationReport(report)) {
|
|
2532
|
-
throw new Error("Invalid UI observation report");
|
|
2533
|
-
}
|
|
2534
|
-
ensureUiObserverDir();
|
|
2535
|
-
appendFileSync2(
|
|
2536
|
-
getUiObservationPath(report.sessionId),
|
|
2537
|
-
`${JSON.stringify(report)}
|
|
2538
|
-
`,
|
|
2539
|
-
{ encoding: "utf8", mode: 384 }
|
|
2540
|
-
);
|
|
2541
|
-
}
|
|
2542
|
-
|
|
2543
2852
|
// src/kernel/observation/ui-observer-synthesis.ts
|
|
2544
2853
|
var MIN_UI_CONFIDENCE = 0.6;
|
|
2545
2854
|
function kindToRating(kind) {
|
|
@@ -3344,7 +3653,7 @@ function generatePrompt(input) {
|
|
|
3344
3653
|
|
|
3345
3654
|
// src/kernel/recall/reference-resolver.ts
|
|
3346
3655
|
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
|
|
3347
|
-
import { dirname as dirname3, join as
|
|
3656
|
+
import { dirname as dirname3, join as join7, resolve } from "path";
|
|
3348
3657
|
var DEFAULT_REVIEW_CONTEXT_MAX_CHARS = 6e3;
|
|
3349
3658
|
function htmlToText(html) {
|
|
3350
3659
|
let content = html;
|
|
@@ -3405,8 +3714,8 @@ async function resolveReference(sourceLink) {
|
|
|
3405
3714
|
const filePath = anchorIndex2 !== -1 ? fullPathWithAnchor.slice(0, anchorIndex2) : fullPathWithAnchor;
|
|
3406
3715
|
const anchor2 = anchorIndex2 !== -1 ? fullPathWithAnchor.slice(anchorIndex2) : "";
|
|
3407
3716
|
const parentDir = dirname3(process.cwd());
|
|
3408
|
-
const localRepoPath =
|
|
3409
|
-
const localFilePath =
|
|
3717
|
+
const localRepoPath = join7(parentDir, repo);
|
|
3718
|
+
const localFilePath = join7(localRepoPath, filePath);
|
|
3410
3719
|
if (existsSync6(localFilePath)) {
|
|
3411
3720
|
try {
|
|
3412
3721
|
let fileContent = readFileSync6(localFilePath, "utf-8");
|
|
@@ -3720,12 +4029,12 @@ import {
|
|
|
3720
4029
|
appendFileSync as appendFileSync3,
|
|
3721
4030
|
copyFileSync,
|
|
3722
4031
|
existsSync as existsSync7,
|
|
3723
|
-
mkdirSync as
|
|
4032
|
+
mkdirSync as mkdirSync6,
|
|
3724
4033
|
readFileSync as readFileSync7,
|
|
3725
|
-
writeFileSync as
|
|
4034
|
+
writeFileSync as writeFileSync4
|
|
3726
4035
|
} from "fs";
|
|
3727
4036
|
import { homedir as homedir5 } from "os";
|
|
3728
|
-
import { join as
|
|
4037
|
+
import { join as join8 } from "path";
|
|
3729
4038
|
import { fileURLToPath } from "url";
|
|
3730
4039
|
var HOME = homedir5();
|
|
3731
4040
|
function getPackageSkillPath(agent = "default") {
|
|
@@ -3733,15 +4042,15 @@ function getPackageSkillPath(agent = "default") {
|
|
|
3733
4042
|
fileURLToPath(new URL("../../..", import.meta.url)),
|
|
3734
4043
|
fileURLToPath(new URL("../..", import.meta.url)),
|
|
3735
4044
|
fileURLToPath(new URL("..", import.meta.url))
|
|
3736
|
-
].find((candidate) => existsSync7(
|
|
4045
|
+
].find((candidate) => existsSync7(join8(candidate, "package.json"))) ?? "";
|
|
3737
4046
|
if (!packageRoot) return "";
|
|
3738
4047
|
if (agent === "codex") {
|
|
3739
|
-
const codexPath =
|
|
4048
|
+
const codexPath = join8(packageRoot, ".agents", "skills", "zam", "SKILL.md");
|
|
3740
4049
|
if (existsSync7(codexPath)) return codexPath;
|
|
3741
4050
|
return "";
|
|
3742
4051
|
}
|
|
3743
4052
|
if (agent === "claude") {
|
|
3744
|
-
const claudePath =
|
|
4053
|
+
const claudePath = join8(
|
|
3745
4054
|
packageRoot,
|
|
3746
4055
|
".claude",
|
|
3747
4056
|
"skills",
|
|
@@ -3751,9 +4060,9 @@ function getPackageSkillPath(agent = "default") {
|
|
|
3751
4060
|
if (existsSync7(claudePath)) return claudePath;
|
|
3752
4061
|
return "";
|
|
3753
4062
|
}
|
|
3754
|
-
let path =
|
|
4063
|
+
let path = join8(packageRoot, ".agent", "skills", "zam", "SKILL.md");
|
|
3755
4064
|
if (existsSync7(path)) return path;
|
|
3756
|
-
path =
|
|
4065
|
+
path = join8(packageRoot, ".claude", "skills", "zam", "SKILL.md");
|
|
3757
4066
|
if (existsSync7(path)) return path;
|
|
3758
4067
|
return "";
|
|
3759
4068
|
}
|
|
@@ -3766,16 +4075,16 @@ function distributeGlobalSkills(home = HOME) {
|
|
|
3766
4075
|
console.warn("Could not find ZAM source SKILL.md in the package folder.");
|
|
3767
4076
|
return results;
|
|
3768
4077
|
}
|
|
3769
|
-
const claudeSkillsDir =
|
|
4078
|
+
const claudeSkillsDir = join8(home, ".claude", "skills", "zam");
|
|
3770
4079
|
try {
|
|
3771
4080
|
if (!claudeSourceSkill) {
|
|
3772
4081
|
throw new Error("Claude skill source not found");
|
|
3773
4082
|
}
|
|
3774
|
-
|
|
3775
|
-
copyFileSync(claudeSourceSkill,
|
|
4083
|
+
mkdirSync6(claudeSkillsDir, { recursive: true });
|
|
4084
|
+
copyFileSync(claudeSourceSkill, join8(claudeSkillsDir, "SKILL.md"));
|
|
3776
4085
|
results.push({
|
|
3777
4086
|
name: "Claude Code Global",
|
|
3778
|
-
path:
|
|
4087
|
+
path: join8(claudeSkillsDir, "SKILL.md"),
|
|
3779
4088
|
success: true
|
|
3780
4089
|
});
|
|
3781
4090
|
} catch (_err) {
|
|
@@ -3785,13 +4094,13 @@ function distributeGlobalSkills(home = HOME) {
|
|
|
3785
4094
|
success: false
|
|
3786
4095
|
});
|
|
3787
4096
|
}
|
|
3788
|
-
const geminiSkillsDir =
|
|
4097
|
+
const geminiSkillsDir = join8(home, ".gemini", "skills", "zam");
|
|
3789
4098
|
try {
|
|
3790
|
-
|
|
3791
|
-
copyFileSync(sourceSkill,
|
|
4099
|
+
mkdirSync6(geminiSkillsDir, { recursive: true });
|
|
4100
|
+
copyFileSync(sourceSkill, join8(geminiSkillsDir, "SKILL.md"));
|
|
3792
4101
|
results.push({
|
|
3793
4102
|
name: "Gemini CLI Global",
|
|
3794
|
-
path:
|
|
4103
|
+
path: join8(geminiSkillsDir, "SKILL.md"),
|
|
3795
4104
|
success: true
|
|
3796
4105
|
});
|
|
3797
4106
|
} catch (_err) {
|
|
@@ -3801,16 +4110,16 @@ function distributeGlobalSkills(home = HOME) {
|
|
|
3801
4110
|
success: false
|
|
3802
4111
|
});
|
|
3803
4112
|
}
|
|
3804
|
-
const codexSkillsDir =
|
|
4113
|
+
const codexSkillsDir = join8(home, ".agents", "skills", "zam");
|
|
3805
4114
|
try {
|
|
3806
4115
|
if (!codexSourceSkill) {
|
|
3807
4116
|
throw new Error("Codex skill source not found");
|
|
3808
4117
|
}
|
|
3809
|
-
|
|
3810
|
-
copyFileSync(codexSourceSkill,
|
|
4118
|
+
mkdirSync6(codexSkillsDir, { recursive: true });
|
|
4119
|
+
copyFileSync(codexSourceSkill, join8(codexSkillsDir, "SKILL.md"));
|
|
3811
4120
|
results.push({
|
|
3812
4121
|
name: "Codex Global",
|
|
3813
|
-
path:
|
|
4122
|
+
path: join8(codexSkillsDir, "SKILL.md"),
|
|
3814
4123
|
success: true
|
|
3815
4124
|
});
|
|
3816
4125
|
} catch (_err) {
|
|
@@ -3820,13 +4129,13 @@ function distributeGlobalSkills(home = HOME) {
|
|
|
3820
4129
|
success: false
|
|
3821
4130
|
});
|
|
3822
4131
|
}
|
|
3823
|
-
const gooseSkillsDir =
|
|
4132
|
+
const gooseSkillsDir = join8(home, ".goose", "skills", "zam");
|
|
3824
4133
|
try {
|
|
3825
|
-
|
|
3826
|
-
copyFileSync(sourceSkill,
|
|
4134
|
+
mkdirSync6(gooseSkillsDir, { recursive: true });
|
|
4135
|
+
copyFileSync(sourceSkill, join8(gooseSkillsDir, "SKILL.md"));
|
|
3827
4136
|
results.push({
|
|
3828
4137
|
name: "Goose Global",
|
|
3829
|
-
path:
|
|
4138
|
+
path: join8(gooseSkillsDir, "SKILL.md"),
|
|
3830
4139
|
success: true
|
|
3831
4140
|
});
|
|
3832
4141
|
} catch (_err) {
|
|
@@ -3875,7 +4184,7 @@ function installHook(file, hook, oldHook) {
|
|
|
3875
4184
|
return { success: true, alreadyHooked: true };
|
|
3876
4185
|
}
|
|
3877
4186
|
if (content.includes(oldHook.trim())) {
|
|
3878
|
-
|
|
4187
|
+
writeFileSync4(file, content.replace(oldHook.trim(), hook.trim()), "utf8");
|
|
3879
4188
|
} else {
|
|
3880
4189
|
appendFileSync3(file, hook);
|
|
3881
4190
|
}
|
|
@@ -3886,24 +4195,24 @@ function installHook(file, hook, oldHook) {
|
|
|
3886
4195
|
}
|
|
3887
4196
|
function injectShellHooks(home = HOME) {
|
|
3888
4197
|
const results = [];
|
|
3889
|
-
const zshrc =
|
|
4198
|
+
const zshrc = join8(home, ".zshrc");
|
|
3890
4199
|
if (existsSync7(zshrc)) {
|
|
3891
4200
|
const status = installHook(zshrc, posixHook("zsh"), POSIX_OLD_HOOK);
|
|
3892
4201
|
results.push({ shell: "zsh", file: zshrc, ...status });
|
|
3893
4202
|
}
|
|
3894
|
-
const bashrc =
|
|
4203
|
+
const bashrc = join8(home, ".bashrc");
|
|
3895
4204
|
if (existsSync7(bashrc)) {
|
|
3896
4205
|
const status = installHook(bashrc, posixHook("bash"), POSIX_OLD_HOOK);
|
|
3897
4206
|
results.push({ shell: "bash", file: bashrc, ...status });
|
|
3898
4207
|
}
|
|
3899
4208
|
const pwshDirs = [
|
|
3900
|
-
|
|
3901
|
-
|
|
4209
|
+
join8(home, "Documents", "PowerShell"),
|
|
4210
|
+
join8(home, "Documents", "WindowsPowerShell")
|
|
3902
4211
|
];
|
|
3903
4212
|
for (const dir of pwshDirs) {
|
|
3904
|
-
const profileFile =
|
|
4213
|
+
const profileFile = join8(dir, "Microsoft.PowerShell_profile.ps1");
|
|
3905
4214
|
try {
|
|
3906
|
-
|
|
4215
|
+
mkdirSync6(dir, { recursive: true });
|
|
3907
4216
|
const status = installHook(
|
|
3908
4217
|
profileFile,
|
|
3909
4218
|
POWERSHELL_HOOK,
|
|
@@ -4135,10 +4444,10 @@ function t(locale, key, params = {}) {
|
|
|
4135
4444
|
}
|
|
4136
4445
|
|
|
4137
4446
|
// src/kernel/system/install-config.ts
|
|
4138
|
-
import { existsSync as existsSync8, mkdirSync as
|
|
4447
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "fs";
|
|
4139
4448
|
import { homedir as homedir6 } from "os";
|
|
4140
|
-
import { dirname as dirname4, join as
|
|
4141
|
-
var DEFAULT_CONFIG_PATH =
|
|
4449
|
+
import { dirname as dirname4, join as join9 } from "path";
|
|
4450
|
+
var DEFAULT_CONFIG_PATH = join9(homedir6(), ".zam", "config.json");
|
|
4142
4451
|
function loadInstallConfig(path = DEFAULT_CONFIG_PATH) {
|
|
4143
4452
|
if (!existsSync8(path)) return {};
|
|
4144
4453
|
try {
|
|
@@ -4149,8 +4458,8 @@ function loadInstallConfig(path = DEFAULT_CONFIG_PATH) {
|
|
|
4149
4458
|
}
|
|
4150
4459
|
function saveInstallConfig(config, path = DEFAULT_CONFIG_PATH) {
|
|
4151
4460
|
const dir = dirname4(path);
|
|
4152
|
-
if (!existsSync8(dir))
|
|
4153
|
-
|
|
4461
|
+
if (!existsSync8(dir)) mkdirSync7(dir, { recursive: true });
|
|
4462
|
+
writeFileSync5(path, `${JSON.stringify(config, null, 2)}
|
|
4154
4463
|
`, "utf-8");
|
|
4155
4464
|
}
|
|
4156
4465
|
function getInstallMode(path = DEFAULT_CONFIG_PATH) {
|
|
@@ -4188,7 +4497,7 @@ function detectSyncProvider(dir) {
|
|
|
4188
4497
|
import { execFileSync, execSync } from "child_process";
|
|
4189
4498
|
import { existsSync as existsSync9 } from "fs";
|
|
4190
4499
|
import { homedir as homedir7 } from "os";
|
|
4191
|
-
import { join as
|
|
4500
|
+
import { join as join10 } from "path";
|
|
4192
4501
|
function hasCommand(cmd) {
|
|
4193
4502
|
try {
|
|
4194
4503
|
const checkCmd = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
|
|
@@ -4233,7 +4542,7 @@ function installOllama() {
|
|
|
4233
4542
|
const isMac = process.platform === "darwin";
|
|
4234
4543
|
const isWin = process.platform === "win32";
|
|
4235
4544
|
const hasOllama = hasCommand("ollama") || isMac && existsSync9("/Applications/Ollama.app") || isWin && existsSync9(
|
|
4236
|
-
|
|
4545
|
+
join10(homedir7(), "AppData", "Local", "Programs", "Ollama", "ollama.exe")
|
|
4237
4546
|
);
|
|
4238
4547
|
if (hasOllama) {
|
|
4239
4548
|
return { success: true, message: "Ollama is already installed." };
|
|
@@ -4293,7 +4602,7 @@ function installOllama() {
|
|
|
4293
4602
|
function resolveOllamaCommand() {
|
|
4294
4603
|
if (hasCommand("ollama")) return "ollama";
|
|
4295
4604
|
const candidates = process.platform === "win32" ? [
|
|
4296
|
-
|
|
4605
|
+
join10(
|
|
4297
4606
|
homedir7(),
|
|
4298
4607
|
"AppData",
|
|
4299
4608
|
"Local",
|
|
@@ -4625,9 +4934,45 @@ function decideUpdate(input) {
|
|
|
4625
4934
|
};
|
|
4626
4935
|
}
|
|
4627
4936
|
}
|
|
4937
|
+
function planUpdate(decision) {
|
|
4938
|
+
if (!decision.updateAvailable) return [];
|
|
4939
|
+
switch (decision.channel) {
|
|
4940
|
+
case "developer":
|
|
4941
|
+
return [
|
|
4942
|
+
{ kind: "git-pull", label: "Pull the latest source" },
|
|
4943
|
+
{ kind: "npm-install", label: "Install dependencies" },
|
|
4944
|
+
{ kind: "npm-build", label: "Rebuild the CLI" },
|
|
4945
|
+
{
|
|
4946
|
+
kind: "distribute-skills",
|
|
4947
|
+
label: "Refresh skill files (zam setup --force)"
|
|
4948
|
+
}
|
|
4949
|
+
];
|
|
4950
|
+
case "winget":
|
|
4951
|
+
case "homebrew":
|
|
4952
|
+
return [
|
|
4953
|
+
{
|
|
4954
|
+
kind: "run-command",
|
|
4955
|
+
label: `Update via ${decision.channel}`,
|
|
4956
|
+
command: decision.command
|
|
4957
|
+
}
|
|
4958
|
+
];
|
|
4959
|
+
case "direct":
|
|
4960
|
+
return [
|
|
4961
|
+
{
|
|
4962
|
+
kind: "self-update",
|
|
4963
|
+
label: "Apply the signed update via the desktop app"
|
|
4964
|
+
}
|
|
4965
|
+
];
|
|
4966
|
+
}
|
|
4967
|
+
}
|
|
4628
4968
|
export {
|
|
4969
|
+
BUILT_IN_SENSITIVE_MATCHERS,
|
|
4970
|
+
DEFAULT_OBSERVER_POLICY,
|
|
4629
4971
|
DEFAULT_REVIEW_CONTEXT_MAX_CHARS,
|
|
4630
4972
|
HOMEBREW_CASK,
|
|
4973
|
+
OBSERVER_POLICY_UNSET_HINT,
|
|
4974
|
+
OBSERVER_POLICY_VERSION,
|
|
4975
|
+
SIDECAR_POLICY_FILE,
|
|
4631
4976
|
SNAPSHOT_VERSION,
|
|
4632
4977
|
UI_OBSERVATION_PROTOCOL_VERSION,
|
|
4633
4978
|
WINGET_PACKAGE_ID,
|
|
@@ -4645,6 +4990,8 @@ export {
|
|
|
4645
4990
|
createFSRS,
|
|
4646
4991
|
createGoal,
|
|
4647
4992
|
createToken,
|
|
4993
|
+
decidePostCapture,
|
|
4994
|
+
decidePreCapture,
|
|
4648
4995
|
decideUpdate,
|
|
4649
4996
|
deleteCardForUser,
|
|
4650
4997
|
deleteSetting,
|
|
@@ -4681,6 +5028,7 @@ export {
|
|
|
4681
5028
|
getCard,
|
|
4682
5029
|
getCardById,
|
|
4683
5030
|
getCardDeletionImpact,
|
|
5031
|
+
getDatabaseTargetInfo,
|
|
4684
5032
|
getDefaultDbPath,
|
|
4685
5033
|
getDependents,
|
|
4686
5034
|
getDomainCompetence,
|
|
@@ -4716,6 +5064,7 @@ export {
|
|
|
4716
5064
|
installOllama,
|
|
4717
5065
|
installOpenCode,
|
|
4718
5066
|
interleave,
|
|
5067
|
+
isObserverPolicyConfigured,
|
|
4719
5068
|
isUiObservationReport,
|
|
4720
5069
|
listAgentSkills,
|
|
4721
5070
|
listGoals,
|
|
@@ -4725,6 +5074,8 @@ export {
|
|
|
4725
5074
|
loadInstallConfig,
|
|
4726
5075
|
logReview,
|
|
4727
5076
|
logStep,
|
|
5077
|
+
matchBuiltInSensitive,
|
|
5078
|
+
matchDenylist,
|
|
4728
5079
|
matchesFilePath,
|
|
4729
5080
|
monitorLogExists,
|
|
4730
5081
|
normalizeLocale,
|
|
@@ -4735,15 +5086,19 @@ export {
|
|
|
4735
5086
|
pairCommands,
|
|
4736
5087
|
parseGoalFile,
|
|
4737
5088
|
parseMonitorLog,
|
|
5089
|
+
parseObserverList,
|
|
5090
|
+
parseObserverPolicy,
|
|
4738
5091
|
parseSnapshot,
|
|
4739
5092
|
parseUiObservationLog,
|
|
4740
5093
|
planOpenCodeInstall,
|
|
5094
|
+
planUpdate,
|
|
4741
5095
|
prepareLocalModel,
|
|
4742
5096
|
prepareSessionSynthesis,
|
|
4743
5097
|
readMonitorLog,
|
|
4744
5098
|
readUiObservationLog,
|
|
4745
5099
|
resolveAllBeliefPaths,
|
|
4746
5100
|
resolveAllGoalPaths,
|
|
5101
|
+
resolveObserverPolicy,
|
|
4747
5102
|
resolveReference,
|
|
4748
5103
|
resolveRepoPath,
|
|
4749
5104
|
resolveReviewContext,
|
|
@@ -4756,7 +5111,9 @@ export {
|
|
|
4756
5111
|
setSetting,
|
|
4757
5112
|
setTursoCredentials,
|
|
4758
5113
|
startSession,
|
|
5114
|
+
syncObserverSidecarPolicy,
|
|
4759
5115
|
t,
|
|
5116
|
+
toSidecarPrivacyPolicy,
|
|
4760
5117
|
uiObservationLogExists,
|
|
4761
5118
|
uiObservationTimeSpan,
|
|
4762
5119
|
unblockReady,
|