zam-core 0.3.11 → 0.3.13

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/dist/index.js CHANGED
@@ -1347,8 +1347,17 @@ async function deleteCardForUser(db, tokenId, userId) {
1347
1347
  await db.prepare("DELETE FROM cards WHERE id = ?").run(card.id);
1348
1348
  return { card, impact };
1349
1349
  }
1350
- async function getDueCards(db, userId, now) {
1350
+ async function getDueCards(db, userId, now, domain) {
1351
1351
  const cutoff = now ?? (/* @__PURE__ */ new Date()).toISOString();
1352
+ if (domain) {
1353
+ return await db.prepare(
1354
+ `SELECT c.*, t.slug, t.concept, t.domain, t.bloom_level
1355
+ FROM cards c
1356
+ JOIN tokens t ON t.id = c.token_id
1357
+ WHERE c.user_id = ? AND c.blocked = 0 AND c.due_at <= ? AND t.domain = ?
1358
+ ORDER BY t.bloom_level ASC, c.due_at ASC`
1359
+ ).all(userId, cutoff, domain);
1360
+ }
1352
1361
  return await db.prepare(
1353
1362
  `SELECT c.*, t.slug, t.concept, t.domain, t.bloom_level
1354
1363
  FROM cards c
@@ -1932,11 +1941,11 @@ function analyzeObservation(commands, tokenPatterns) {
1932
1941
  const ratings = [];
1933
1942
  for (const tp of tokenPatterns) {
1934
1943
  const matchIndices = [];
1935
- const matchedTexts = [];
1944
+ const matchedTexts2 = [];
1936
1945
  for (let i = 0; i < commands.length; i++) {
1937
1946
  if (matchesToken(commands[i].command, tp.patterns)) {
1938
1947
  matchIndices.push(i);
1939
- matchedTexts.push(commands[i].command);
1948
+ matchedTexts2.push(commands[i].command);
1940
1949
  matchedSet.add(i);
1941
1950
  }
1942
1951
  }
@@ -2026,7 +2035,7 @@ function analyzeObservation(commands, tokenPatterns) {
2026
2035
  medianGapMs,
2027
2036
  thinkingGapMs
2028
2037
  },
2029
- matchedCommandTexts: matchedTexts
2038
+ matchedCommandTexts: matchedTexts2
2030
2039
  });
2031
2040
  }
2032
2041
  const unmatchedCommands = [];
@@ -2391,6 +2400,240 @@ async function unblockReady(db, userId) {
2391
2400
  return { unblocked };
2392
2401
  }
2393
2402
 
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
+ // src/kernel/observation/ui-observer-synthesis.ts
2544
+ var MIN_UI_CONFIDENCE = 0.6;
2545
+ function kindToRating(kind) {
2546
+ switch (kind) {
2547
+ case "step-completed":
2548
+ return 4;
2549
+ case "progress":
2550
+ return 3;
2551
+ case "error":
2552
+ case "help-seeking":
2553
+ return 2;
2554
+ default:
2555
+ return null;
2556
+ }
2557
+ }
2558
+ function toSynthesisConfidence(confidence) {
2559
+ return confidence >= 0.85 ? "high" : "medium";
2560
+ }
2561
+ function confidenceRank(confidence) {
2562
+ return confidence === "high" ? 2 : 1;
2563
+ }
2564
+ function buildEvidence(report) {
2565
+ return {
2566
+ matchedCommands: report.actions.length,
2567
+ helpSeeking: report.kind === "help-seeking",
2568
+ errorCount: report.kind === "error" ? 1 : 0,
2569
+ selfCorrections: 0,
2570
+ medianGapMs: null,
2571
+ thinkingGapMs: null
2572
+ };
2573
+ }
2574
+ function matchedTexts(report) {
2575
+ const texts = [report.summary];
2576
+ for (const action of report.actions) {
2577
+ const label = [action.type, action.target, action.result].filter(Boolean).join(" ");
2578
+ if (label) texts.push(label);
2579
+ }
2580
+ return texts;
2581
+ }
2582
+ function buildUiSynthesisCandidates(reports, tokens, applied, minConfidence) {
2583
+ const minRank = confidenceRank(minConfidence);
2584
+ const bestBySlug = /* @__PURE__ */ new Map();
2585
+ let skippedLowConfidence = 0;
2586
+ for (const report of reports) {
2587
+ if (report.confidence < MIN_UI_CONFIDENCE) continue;
2588
+ const fallbackRating = kindToRating(report.kind);
2589
+ const candidates2 = report.candidateTokens.length > 0 ? report.candidateTokens.map((candidate) => ({
2590
+ slug: candidate.slug,
2591
+ confidence: candidate.confidence,
2592
+ rating: fallbackRating
2593
+ })) : [];
2594
+ for (const candidate of candidates2) {
2595
+ const token = tokens.get(candidate.slug);
2596
+ if (!token || applied.has(token.id)) continue;
2597
+ const synthesisConfidence = toSynthesisConfidence(candidate.confidence);
2598
+ if (confidenceRank(synthesisConfidence) < minRank) {
2599
+ skippedLowConfidence++;
2600
+ continue;
2601
+ }
2602
+ const inferredRating = candidate.rating ?? 3;
2603
+ const existing = bestBySlug.get(candidate.slug);
2604
+ if (existing && existing.reportConfidence >= candidate.confidence) {
2605
+ continue;
2606
+ }
2607
+ bestBySlug.set(candidate.slug, {
2608
+ token,
2609
+ inferredRating,
2610
+ confidence: synthesisConfidence,
2611
+ evidence: buildEvidence(report),
2612
+ matchedCommandTexts: matchedTexts(report),
2613
+ reportConfidence: candidate.confidence
2614
+ });
2615
+ }
2616
+ }
2617
+ const candidates = [...bestBySlug.values()].sort((left, right) => right.reportConfidence - left.reportConfidence).map((entry) => ({
2618
+ tokenId: entry.token.id,
2619
+ tokenSlug: entry.token.slug,
2620
+ concept: entry.token.concept,
2621
+ domain: entry.token.domain,
2622
+ inferredRating: entry.inferredRating,
2623
+ confidence: entry.confidence,
2624
+ evidence: entry.evidence,
2625
+ matchedCommandTexts: entry.matchedCommandTexts
2626
+ }));
2627
+ return { candidates, skippedLowConfidence };
2628
+ }
2629
+ function uiObservationTimeSpan(reports) {
2630
+ if (reports.length === 0) return null;
2631
+ const start = reports[0].observedFrom;
2632
+ const end = reports[reports.length - 1].observedTo;
2633
+ const durationMs = Math.max(0, Date.parse(end) - Date.parse(start));
2634
+ return { start, end, durationMs };
2635
+ }
2636
+
2394
2637
  // src/kernel/observation/session-synthesis.ts
2395
2638
  function parseSynthesisRow(row) {
2396
2639
  return {
@@ -2398,7 +2641,7 @@ function parseSynthesisRow(row) {
2398
2641
  evidence: JSON.parse(row.evidence)
2399
2642
  };
2400
2643
  }
2401
- function confidenceRank(confidence) {
2644
+ function confidenceRank2(confidence) {
2402
2645
  return confidence === "high" ? 2 : confidence === "medium" ? 1 : 0;
2403
2646
  }
2404
2647
  function normalizeSkillStep(step) {
@@ -2460,20 +2703,50 @@ async function prepareSessionSynthesis(db, input) {
2460
2703
  validPatterns.push(pattern);
2461
2704
  tokens.set(pattern.slug, token);
2462
2705
  }
2463
- const commands = input.commands ?? pairCommands(readMonitorLog(input.sessionId));
2464
- const analysis = analyzeObservation(commands, validPatterns);
2465
2706
  const applied = new Set(
2466
2707
  (await getSessionSynthesisRecords(db, input.sessionId)).map(
2467
2708
  (record) => record.token_id
2468
2709
  )
2469
2710
  );
2470
- const minRank = confidenceRank(input.minConfidence ?? "medium");
2711
+ const minConfidence = input.minConfidence ?? "medium";
2712
+ if (session.execution_context === "ui") {
2713
+ const reports = readUiObservationLog(input.sessionId);
2714
+ for (const report of reports) {
2715
+ for (const candidate of report.candidateTokens) {
2716
+ if (tokens.has(candidate.slug)) continue;
2717
+ const token = await getTokenBySlug(db, candidate.slug);
2718
+ if (token && !token.deprecated_at) {
2719
+ tokens.set(candidate.slug, token);
2720
+ }
2721
+ }
2722
+ }
2723
+ const { candidates: candidates2, skippedLowConfidence: skippedLowConfidence2 } = buildUiSynthesisCandidates(
2724
+ reports,
2725
+ tokens,
2726
+ applied,
2727
+ minConfidence
2728
+ );
2729
+ return {
2730
+ sessionId: session.id,
2731
+ userId: session.user_id,
2732
+ patternCount: validPatterns.length,
2733
+ commandCount: reports.length,
2734
+ alreadyApplied: applied.size,
2735
+ skippedLowConfidence: skippedLowConfidence2,
2736
+ candidates: candidates2,
2737
+ unmatchedCommands: [],
2738
+ timeSpan: uiObservationTimeSpan(reports)
2739
+ };
2740
+ }
2741
+ const commands = input.commands ?? pairCommands(readMonitorLog(input.sessionId));
2742
+ const analysis = analyzeObservation(commands, validPatterns);
2743
+ const minRank = confidenceRank2(minConfidence);
2471
2744
  let skippedLowConfidence = 0;
2472
2745
  const candidates = [];
2473
2746
  for (const rating of analysis.ratings) {
2474
2747
  const token = tokens.get(rating.tokenSlug);
2475
2748
  if (!token || rating.rating == null || applied.has(token.id)) continue;
2476
- if (confidenceRank(rating.confidence) < minRank) {
2749
+ if (confidenceRank2(rating.confidence) < minRank) {
2477
2750
  skippedLowConfidence++;
2478
2751
  continue;
2479
2752
  }
@@ -3070,8 +3343,8 @@ function generatePrompt(input) {
3070
3343
  }
3071
3344
 
3072
3345
  // src/kernel/recall/reference-resolver.ts
3073
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
3074
- import { dirname as dirname3, join as join5, resolve } from "path";
3346
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
3347
+ import { dirname as dirname3, join as join6, resolve } from "path";
3075
3348
  var DEFAULT_REVIEW_CONTEXT_MAX_CHARS = 6e3;
3076
3349
  function htmlToText(html) {
3077
3350
  let content = html;
@@ -3132,11 +3405,11 @@ async function resolveReference(sourceLink) {
3132
3405
  const filePath = anchorIndex2 !== -1 ? fullPathWithAnchor.slice(0, anchorIndex2) : fullPathWithAnchor;
3133
3406
  const anchor2 = anchorIndex2 !== -1 ? fullPathWithAnchor.slice(anchorIndex2) : "";
3134
3407
  const parentDir = dirname3(process.cwd());
3135
- const localRepoPath = join5(parentDir, repo);
3136
- const localFilePath = join5(localRepoPath, filePath);
3137
- if (existsSync5(localFilePath)) {
3408
+ const localRepoPath = join6(parentDir, repo);
3409
+ const localFilePath = join6(localRepoPath, filePath);
3410
+ if (existsSync6(localFilePath)) {
3138
3411
  try {
3139
- let fileContent = readFileSync5(localFilePath, "utf-8");
3412
+ let fileContent = readFileSync6(localFilePath, "utf-8");
3140
3413
  if (anchor2) {
3141
3414
  fileContent = extractLines(fileContent, anchor2);
3142
3415
  }
@@ -3190,9 +3463,9 @@ Link: ${cleaned}`,
3190
3463
  const relativePath = anchorIndex !== -1 ? cleaned.slice(0, anchorIndex) : cleaned;
3191
3464
  const anchor = anchorIndex !== -1 ? cleaned.slice(anchorIndex) : "";
3192
3465
  const absolutePath = resolve(process.cwd(), relativePath);
3193
- if (existsSync5(absolutePath)) {
3466
+ if (existsSync6(absolutePath)) {
3194
3467
  try {
3195
- let fileContent = readFileSync5(absolutePath, "utf-8");
3468
+ let fileContent = readFileSync6(absolutePath, "utf-8");
3196
3469
  if (anchor) {
3197
3470
  fileContent = extractLines(fileContent, anchor);
3198
3471
  }
@@ -3444,44 +3717,44 @@ function intersperseNew(reviews, newCards, interval) {
3444
3717
 
3445
3718
  // src/kernel/system/hooks.ts
3446
3719
  import {
3447
- appendFileSync as appendFileSync2,
3720
+ appendFileSync as appendFileSync3,
3448
3721
  copyFileSync,
3449
- existsSync as existsSync6,
3450
- mkdirSync as mkdirSync4,
3451
- readFileSync as readFileSync6,
3722
+ existsSync as existsSync7,
3723
+ mkdirSync as mkdirSync5,
3724
+ readFileSync as readFileSync7,
3452
3725
  writeFileSync as writeFileSync3
3453
3726
  } from "fs";
3454
- import { homedir as homedir4 } from "os";
3455
- import { join as join6 } from "path";
3727
+ import { homedir as homedir5 } from "os";
3728
+ import { join as join7 } from "path";
3456
3729
  import { fileURLToPath } from "url";
3457
- var HOME = homedir4();
3730
+ var HOME = homedir5();
3458
3731
  function getPackageSkillPath(agent = "default") {
3459
3732
  const packageRoot = [
3460
3733
  fileURLToPath(new URL("../../..", import.meta.url)),
3461
3734
  fileURLToPath(new URL("../..", import.meta.url)),
3462
3735
  fileURLToPath(new URL("..", import.meta.url))
3463
- ].find((candidate) => existsSync6(join6(candidate, "package.json"))) ?? "";
3736
+ ].find((candidate) => existsSync7(join7(candidate, "package.json"))) ?? "";
3464
3737
  if (!packageRoot) return "";
3465
3738
  if (agent === "codex") {
3466
- const codexPath = join6(packageRoot, ".agents", "skills", "zam", "SKILL.md");
3467
- if (existsSync6(codexPath)) return codexPath;
3739
+ const codexPath = join7(packageRoot, ".agents", "skills", "zam", "SKILL.md");
3740
+ if (existsSync7(codexPath)) return codexPath;
3468
3741
  return "";
3469
3742
  }
3470
3743
  if (agent === "claude") {
3471
- const claudePath = join6(
3744
+ const claudePath = join7(
3472
3745
  packageRoot,
3473
3746
  ".claude",
3474
3747
  "skills",
3475
3748
  "zam",
3476
3749
  "SKILL.md"
3477
3750
  );
3478
- if (existsSync6(claudePath)) return claudePath;
3751
+ if (existsSync7(claudePath)) return claudePath;
3479
3752
  return "";
3480
3753
  }
3481
- let path = join6(packageRoot, ".agent", "skills", "zam", "SKILL.md");
3482
- if (existsSync6(path)) return path;
3483
- path = join6(packageRoot, ".claude", "skills", "zam", "SKILL.md");
3484
- if (existsSync6(path)) return path;
3754
+ let path = join7(packageRoot, ".agent", "skills", "zam", "SKILL.md");
3755
+ if (existsSync7(path)) return path;
3756
+ path = join7(packageRoot, ".claude", "skills", "zam", "SKILL.md");
3757
+ if (existsSync7(path)) return path;
3485
3758
  return "";
3486
3759
  }
3487
3760
  function distributeGlobalSkills(home = HOME) {
@@ -3493,16 +3766,16 @@ function distributeGlobalSkills(home = HOME) {
3493
3766
  console.warn("Could not find ZAM source SKILL.md in the package folder.");
3494
3767
  return results;
3495
3768
  }
3496
- const claudeSkillsDir = join6(home, ".claude", "skills", "zam");
3769
+ const claudeSkillsDir = join7(home, ".claude", "skills", "zam");
3497
3770
  try {
3498
3771
  if (!claudeSourceSkill) {
3499
3772
  throw new Error("Claude skill source not found");
3500
3773
  }
3501
- mkdirSync4(claudeSkillsDir, { recursive: true });
3502
- copyFileSync(claudeSourceSkill, join6(claudeSkillsDir, "SKILL.md"));
3774
+ mkdirSync5(claudeSkillsDir, { recursive: true });
3775
+ copyFileSync(claudeSourceSkill, join7(claudeSkillsDir, "SKILL.md"));
3503
3776
  results.push({
3504
3777
  name: "Claude Code Global",
3505
- path: join6(claudeSkillsDir, "SKILL.md"),
3778
+ path: join7(claudeSkillsDir, "SKILL.md"),
3506
3779
  success: true
3507
3780
  });
3508
3781
  } catch (_err) {
@@ -3512,13 +3785,13 @@ function distributeGlobalSkills(home = HOME) {
3512
3785
  success: false
3513
3786
  });
3514
3787
  }
3515
- const geminiSkillsDir = join6(home, ".gemini", "skills", "zam");
3788
+ const geminiSkillsDir = join7(home, ".gemini", "skills", "zam");
3516
3789
  try {
3517
- mkdirSync4(geminiSkillsDir, { recursive: true });
3518
- copyFileSync(sourceSkill, join6(geminiSkillsDir, "SKILL.md"));
3790
+ mkdirSync5(geminiSkillsDir, { recursive: true });
3791
+ copyFileSync(sourceSkill, join7(geminiSkillsDir, "SKILL.md"));
3519
3792
  results.push({
3520
3793
  name: "Gemini CLI Global",
3521
- path: join6(geminiSkillsDir, "SKILL.md"),
3794
+ path: join7(geminiSkillsDir, "SKILL.md"),
3522
3795
  success: true
3523
3796
  });
3524
3797
  } catch (_err) {
@@ -3528,16 +3801,16 @@ function distributeGlobalSkills(home = HOME) {
3528
3801
  success: false
3529
3802
  });
3530
3803
  }
3531
- const codexSkillsDir = join6(home, ".agents", "skills", "zam");
3804
+ const codexSkillsDir = join7(home, ".agents", "skills", "zam");
3532
3805
  try {
3533
3806
  if (!codexSourceSkill) {
3534
3807
  throw new Error("Codex skill source not found");
3535
3808
  }
3536
- mkdirSync4(codexSkillsDir, { recursive: true });
3537
- copyFileSync(codexSourceSkill, join6(codexSkillsDir, "SKILL.md"));
3809
+ mkdirSync5(codexSkillsDir, { recursive: true });
3810
+ copyFileSync(codexSourceSkill, join7(codexSkillsDir, "SKILL.md"));
3538
3811
  results.push({
3539
3812
  name: "Codex Global",
3540
- path: join6(codexSkillsDir, "SKILL.md"),
3813
+ path: join7(codexSkillsDir, "SKILL.md"),
3541
3814
  success: true
3542
3815
  });
3543
3816
  } catch (_err) {
@@ -3547,13 +3820,13 @@ function distributeGlobalSkills(home = HOME) {
3547
3820
  success: false
3548
3821
  });
3549
3822
  }
3550
- const gooseSkillsDir = join6(home, ".goose", "skills", "zam");
3823
+ const gooseSkillsDir = join7(home, ".goose", "skills", "zam");
3551
3824
  try {
3552
- mkdirSync4(gooseSkillsDir, { recursive: true });
3553
- copyFileSync(sourceSkill, join6(gooseSkillsDir, "SKILL.md"));
3825
+ mkdirSync5(gooseSkillsDir, { recursive: true });
3826
+ copyFileSync(sourceSkill, join7(gooseSkillsDir, "SKILL.md"));
3554
3827
  results.push({
3555
3828
  name: "Goose Global",
3556
- path: join6(gooseSkillsDir, "SKILL.md"),
3829
+ path: join7(gooseSkillsDir, "SKILL.md"),
3557
3830
  success: true
3558
3831
  });
3559
3832
  } catch (_err) {
@@ -3597,14 +3870,14 @@ function Start-ZamMonitor {
3597
3870
  `;
3598
3871
  function installHook(file, hook, oldHook) {
3599
3872
  try {
3600
- const content = existsSync6(file) ? readFileSync6(file, "utf8") : "";
3873
+ const content = existsSync7(file) ? readFileSync7(file, "utf8") : "";
3601
3874
  if (content.includes(HOOK_MARKER)) {
3602
3875
  return { success: true, alreadyHooked: true };
3603
3876
  }
3604
3877
  if (content.includes(oldHook.trim())) {
3605
3878
  writeFileSync3(file, content.replace(oldHook.trim(), hook.trim()), "utf8");
3606
3879
  } else {
3607
- appendFileSync2(file, hook);
3880
+ appendFileSync3(file, hook);
3608
3881
  }
3609
3882
  return { success: true, alreadyHooked: false };
3610
3883
  } catch {
@@ -3613,24 +3886,24 @@ function installHook(file, hook, oldHook) {
3613
3886
  }
3614
3887
  function injectShellHooks(home = HOME) {
3615
3888
  const results = [];
3616
- const zshrc = join6(home, ".zshrc");
3617
- if (existsSync6(zshrc)) {
3889
+ const zshrc = join7(home, ".zshrc");
3890
+ if (existsSync7(zshrc)) {
3618
3891
  const status = installHook(zshrc, posixHook("zsh"), POSIX_OLD_HOOK);
3619
3892
  results.push({ shell: "zsh", file: zshrc, ...status });
3620
3893
  }
3621
- const bashrc = join6(home, ".bashrc");
3622
- if (existsSync6(bashrc)) {
3894
+ const bashrc = join7(home, ".bashrc");
3895
+ if (existsSync7(bashrc)) {
3623
3896
  const status = installHook(bashrc, posixHook("bash"), POSIX_OLD_HOOK);
3624
3897
  results.push({ shell: "bash", file: bashrc, ...status });
3625
3898
  }
3626
3899
  const pwshDirs = [
3627
- join6(home, "Documents", "PowerShell"),
3628
- join6(home, "Documents", "WindowsPowerShell")
3900
+ join7(home, "Documents", "PowerShell"),
3901
+ join7(home, "Documents", "WindowsPowerShell")
3629
3902
  ];
3630
3903
  for (const dir of pwshDirs) {
3631
- const profileFile = join6(dir, "Microsoft.PowerShell_profile.ps1");
3904
+ const profileFile = join7(dir, "Microsoft.PowerShell_profile.ps1");
3632
3905
  try {
3633
- mkdirSync4(dir, { recursive: true });
3906
+ mkdirSync5(dir, { recursive: true });
3634
3907
  const status = installHook(
3635
3908
  profileFile,
3636
3909
  POWERSHELL_HOOK,
@@ -3862,21 +4135,21 @@ function t(locale, key, params = {}) {
3862
4135
  }
3863
4136
 
3864
4137
  // src/kernel/system/install-config.ts
3865
- import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
3866
- import { homedir as homedir5 } from "os";
3867
- import { dirname as dirname4, join as join7 } from "path";
3868
- var DEFAULT_CONFIG_PATH = join7(homedir5(), ".zam", "config.json");
4138
+ import { existsSync as existsSync8, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
4139
+ import { homedir as homedir6 } from "os";
4140
+ import { dirname as dirname4, join as join8 } from "path";
4141
+ var DEFAULT_CONFIG_PATH = join8(homedir6(), ".zam", "config.json");
3869
4142
  function loadInstallConfig(path = DEFAULT_CONFIG_PATH) {
3870
- if (!existsSync7(path)) return {};
4143
+ if (!existsSync8(path)) return {};
3871
4144
  try {
3872
- return JSON.parse(readFileSync7(path, "utf-8"));
4145
+ return JSON.parse(readFileSync8(path, "utf-8"));
3873
4146
  } catch {
3874
4147
  return {};
3875
4148
  }
3876
4149
  }
3877
4150
  function saveInstallConfig(config, path = DEFAULT_CONFIG_PATH) {
3878
4151
  const dir = dirname4(path);
3879
- if (!existsSync7(dir)) mkdirSync5(dir, { recursive: true });
4152
+ if (!existsSync8(dir)) mkdirSync6(dir, { recursive: true });
3880
4153
  writeFileSync4(path, `${JSON.stringify(config, null, 2)}
3881
4154
  `, "utf-8");
3882
4155
  }
@@ -3913,9 +4186,9 @@ function detectSyncProvider(dir) {
3913
4186
 
3914
4187
  // src/kernel/system/installer.ts
3915
4188
  import { execFileSync, execSync } from "child_process";
3916
- import { existsSync as existsSync8 } from "fs";
3917
- import { homedir as homedir6 } from "os";
3918
- import { join as join8 } from "path";
4189
+ import { existsSync as existsSync9 } from "fs";
4190
+ import { homedir as homedir7 } from "os";
4191
+ import { join as join9 } from "path";
3919
4192
  function hasCommand(cmd) {
3920
4193
  try {
3921
4194
  const checkCmd = process.platform === "win32" ? `where ${cmd}` : `which ${cmd}`;
@@ -3932,7 +4205,7 @@ function installFastFlowLM() {
3932
4205
  message: "FastFlowLM is only supported on Windows."
3933
4206
  };
3934
4207
  }
3935
- const hasFlm = hasCommand("flm") || existsSync8("C:\\Program Files\\flm\\flm.exe");
4208
+ const hasFlm = hasCommand("flm") || existsSync9("C:\\Program Files\\flm\\flm.exe");
3936
4209
  if (hasFlm) {
3937
4210
  return { success: true, message: "FastFlowLM is already installed." };
3938
4211
  }
@@ -3959,8 +4232,8 @@ function installFastFlowLM() {
3959
4232
  function installOllama() {
3960
4233
  const isMac = process.platform === "darwin";
3961
4234
  const isWin = process.platform === "win32";
3962
- const hasOllama = hasCommand("ollama") || isMac && existsSync8("/Applications/Ollama.app") || isWin && existsSync8(
3963
- join8(homedir6(), "AppData", "Local", "Programs", "Ollama", "ollama.exe")
4235
+ const hasOllama = hasCommand("ollama") || isMac && existsSync9("/Applications/Ollama.app") || isWin && existsSync9(
4236
+ join9(homedir7(), "AppData", "Local", "Programs", "Ollama", "ollama.exe")
3964
4237
  );
3965
4238
  if (hasOllama) {
3966
4239
  return { success: true, message: "Ollama is already installed." };
@@ -4020,8 +4293,8 @@ function installOllama() {
4020
4293
  function resolveOllamaCommand() {
4021
4294
  if (hasCommand("ollama")) return "ollama";
4022
4295
  const candidates = process.platform === "win32" ? [
4023
- join8(
4024
- homedir6(),
4296
+ join9(
4297
+ homedir7(),
4025
4298
  "AppData",
4026
4299
  "Local",
4027
4300
  "Programs",
@@ -4029,7 +4302,7 @@ function resolveOllamaCommand() {
4029
4302
  "ollama.exe"
4030
4303
  )
4031
4304
  ] : process.platform === "darwin" ? ["/Applications/Ollama.app/Contents/Resources/ollama"] : [];
4032
- return candidates.find((candidate) => existsSync8(candidate));
4305
+ return candidates.find((candidate) => existsSync9(candidate));
4033
4306
  }
4034
4307
  function prepareLocalModel(runner, model) {
4035
4308
  if (runner === "fastflowlm") {
@@ -4217,7 +4490,7 @@ function getSystemProfile() {
4217
4490
  }
4218
4491
 
4219
4492
  // src/kernel/system/repos.ts
4220
- import { existsSync as existsSync9 } from "fs";
4493
+ import { existsSync as existsSync10 } from "fs";
4221
4494
  import { resolve as resolve2 } from "path";
4222
4495
  async function getRepoPaths(db) {
4223
4496
  const personalSetting = await getSetting(db, "repo.personal") || await getSetting(db, "personal.workspace_dir");
@@ -4238,15 +4511,15 @@ async function resolveAllBeliefPaths(db) {
4238
4511
  const dirs = [];
4239
4512
  if (paths.personal) {
4240
4513
  const personalDir = resolve2(paths.personal, "beliefs");
4241
- if (existsSync9(personalDir)) dirs.push(personalDir);
4514
+ if (existsSync10(personalDir)) dirs.push(personalDir);
4242
4515
  }
4243
4516
  if (paths.team) {
4244
4517
  const teamDir = resolve2(paths.team, "beliefs");
4245
- if (existsSync9(teamDir)) dirs.push(teamDir);
4518
+ if (existsSync10(teamDir)) dirs.push(teamDir);
4246
4519
  }
4247
4520
  if (paths.org) {
4248
4521
  const orgDir = resolve2(paths.org, "beliefs");
4249
- if (existsSync9(orgDir)) dirs.push(orgDir);
4522
+ if (existsSync10(orgDir)) dirs.push(orgDir);
4250
4523
  }
4251
4524
  return dirs;
4252
4525
  }
@@ -4255,15 +4528,15 @@ async function resolveAllGoalPaths(db) {
4255
4528
  const dirs = [];
4256
4529
  if (paths.personal) {
4257
4530
  const personalDir = resolve2(paths.personal, "goals");
4258
- if (existsSync9(personalDir)) dirs.push(personalDir);
4531
+ if (existsSync10(personalDir)) dirs.push(personalDir);
4259
4532
  }
4260
4533
  if (paths.team) {
4261
4534
  const teamDir = resolve2(paths.team, "goals");
4262
- if (existsSync9(teamDir)) dirs.push(teamDir);
4535
+ if (existsSync10(teamDir)) dirs.push(teamDir);
4263
4536
  }
4264
4537
  if (paths.org) {
4265
4538
  const orgDir = resolve2(paths.org, "goals");
4266
- if (existsSync9(orgDir)) dirs.push(orgDir);
4539
+ if (existsSync10(orgDir)) dirs.push(orgDir);
4267
4540
  }
4268
4541
  return dirs;
4269
4542
  }
@@ -4356,11 +4629,14 @@ export {
4356
4629
  DEFAULT_REVIEW_CONTEXT_MAX_CHARS,
4357
4630
  HOMEBREW_CASK,
4358
4631
  SNAPSHOT_VERSION,
4632
+ UI_OBSERVATION_PROTOCOL_VERSION,
4359
4633
  WINGET_PACKAGE_ID,
4360
4634
  addPrerequisite,
4361
4635
  analyzeObservation,
4636
+ appendUiObservationReport,
4362
4637
  applySessionSynthesis,
4363
4638
  buildReviewQueue,
4639
+ buildUiSynthesisCandidates,
4364
4640
  cascadeBlock,
4365
4641
  clearADOCredentials,
4366
4642
  clearTursoCredentials,
@@ -4381,6 +4657,7 @@ export {
4381
4657
  endSession,
4382
4658
  ensureCard,
4383
4659
  ensureMonitorDir,
4660
+ ensureUiObserverDir,
4384
4661
  evaluateRating,
4385
4662
  executeReviewAction,
4386
4663
  exportSnapshot,
@@ -4429,6 +4706,8 @@ export {
4429
4706
  getTokenDeleteImpact,
4430
4707
  getTokenNeighborhood,
4431
4708
  getTursoCredentials,
4709
+ getUiObservationPath,
4710
+ getUiObserverDir,
4432
4711
  getUserStats,
4433
4712
  hasCommand,
4434
4713
  importSnapshot,
@@ -4437,6 +4716,7 @@ export {
4437
4716
  installOllama,
4438
4717
  installOpenCode,
4439
4718
  interleave,
4719
+ isUiObservationReport,
4440
4720
  listAgentSkills,
4441
4721
  listGoals,
4442
4722
  listTokens,
@@ -4456,10 +4736,12 @@ export {
4456
4736
  parseGoalFile,
4457
4737
  parseMonitorLog,
4458
4738
  parseSnapshot,
4739
+ parseUiObservationLog,
4459
4740
  planOpenCodeInstall,
4460
4741
  prepareLocalModel,
4461
4742
  prepareSessionSynthesis,
4462
4743
  readMonitorLog,
4744
+ readUiObservationLog,
4463
4745
  resolveAllBeliefPaths,
4464
4746
  resolveAllGoalPaths,
4465
4747
  resolveReference,
@@ -4475,6 +4757,8 @@ export {
4475
4757
  setTursoCredentials,
4476
4758
  startSession,
4477
4759
  t,
4760
+ uiObservationLogExists,
4761
+ uiObservationTimeSpan,
4478
4762
  unblockReady,
4479
4763
  updateCard,
4480
4764
  updateGoalStatus,