@alook/daemon 0.1.31 → 0.1.33

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.
Files changed (3) hide show
  1. package/dist/cli/index.js +1295 -235
  2. package/dist/index.js +1127 -152
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -91,6 +91,12 @@ function nanoid3(size = 21) {
91
91
  }
92
92
  var POOL_SIZE_MULTIPLIER = 128, pool, poolOffset;
93
93
  var init_nanoid = () => {};
94
+
95
+ // agent-driver/dist/contract.js
96
+ var SESSION_FILE_DISCOVERY_CAPABILITIES = ["supported", "unavailable"];
97
+
98
+ // agent-driver/dist/public-contract.js
99
+ var SESSION_FILE_DISCOVERY_CAPABILITIES2 = SESSION_FILE_DISCOVERY_CAPABILITIES;
94
100
  // agent-driver/dist/host/default-host.js
95
101
  import { randomUUID } from "node:crypto";
96
102
  function createDefaultAgentDriverHost(options = {}) {
@@ -1232,6 +1238,12 @@ function tryParseJsonLine(line) {
1232
1238
  return null;
1233
1239
  }
1234
1240
  }
1241
+ function asRecord(value) {
1242
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
1243
+ }
1244
+ function tryParseJsonRecord(line) {
1245
+ return asRecord(tryParseJsonLine(line));
1246
+ }
1235
1247
 
1236
1248
  // agent-driver/dist/adapters/claude/normalizer.js
1237
1249
  var API_ERROR_RE = /API Error:.*(?:Connection error|\b[45]\d{2}\b)/i;
@@ -1730,6 +1742,241 @@ class ClaudeTurnProtocol {
1730
1742
  }
1731
1743
  }
1732
1744
 
1745
+ // agent-driver/dist/adapters/claude/recent-context.js
1746
+ import { createReadStream as createReadStream2 } from "node:fs";
1747
+ import { homedir } from "node:os";
1748
+ import path6 from "node:path";
1749
+ import { createInterface } from "node:readline";
1750
+
1751
+ // agent-driver/dist/internal/recent-context.js
1752
+ import path5 from "node:path";
1753
+ import { createReadStream } from "node:fs";
1754
+ import { readdir, stat } from "node:fs/promises";
1755
+ function normalizeAbsolutePath(value) {
1756
+ if (typeof value !== "string" || !path5.isAbsolute(value))
1757
+ return null;
1758
+ return path5.normalize(value);
1759
+ }
1760
+ function normalizeModifiedAt(value) {
1761
+ const date = value instanceof Date ? value : typeof value === "string" || typeof value === "number" ? new Date(value) : null;
1762
+ if (!date || !Number.isFinite(date.getTime()))
1763
+ return null;
1764
+ return date.toISOString();
1765
+ }
1766
+ function isValidRecentContextTopK(value) {
1767
+ return Number.isSafeInteger(value) && value >= 0;
1768
+ }
1769
+ function isMissingPathError(error) {
1770
+ return error?.code === "ENOENT";
1771
+ }
1772
+ async function readFirstLine(filePath, maxBytes = 65536) {
1773
+ const stream = createReadStream(filePath, { highWaterMark: 1 });
1774
+ const bytes = [];
1775
+ let length = 0;
1776
+ try {
1777
+ for await (const chunk of stream) {
1778
+ const byte = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
1779
+ if (byte[0] === 10)
1780
+ break;
1781
+ if (length >= maxBytes)
1782
+ return null;
1783
+ bytes.push(byte);
1784
+ length += byte.length;
1785
+ }
1786
+ return length === 0 ? null : Buffer.concat(bytes, length).toString("utf8").replace(/\r$/, "");
1787
+ } catch (error) {
1788
+ if (isMissingPathError(error))
1789
+ return null;
1790
+ throw error;
1791
+ } finally {
1792
+ stream.destroy();
1793
+ }
1794
+ }
1795
+ async function collectJsonlFileCandidates(root, options = {}) {
1796
+ const candidates = [];
1797
+ const readDirectory = options.readDirectory ?? ((directory) => readdir(directory, { withFileTypes: true }));
1798
+ const statPath = options.statPath ?? stat;
1799
+ const visit = async (directory, remainingDepth, includeFiles) => {
1800
+ let entries;
1801
+ try {
1802
+ entries = await readDirectory(directory);
1803
+ } catch (error) {
1804
+ if (isMissingPathError(error))
1805
+ return;
1806
+ throw error;
1807
+ }
1808
+ await Promise.all(entries.map(async (entry) => {
1809
+ const entryPath = path5.join(directory, entry.name);
1810
+ if (entry.isDirectory() && remainingDepth > 0) {
1811
+ await visit(entryPath, remainingDepth - 1, true);
1812
+ } else if (includeFiles && entry.isFile() && entry.name.endsWith(".jsonl")) {
1813
+ try {
1814
+ const metadata = await statPath(entryPath);
1815
+ if (metadata.isFile())
1816
+ candidates.push({ filePath: entryPath, modifiedAt: metadata.mtime });
1817
+ } catch (error) {
1818
+ if (!isMissingPathError(error))
1819
+ throw error;
1820
+ }
1821
+ }
1822
+ }));
1823
+ };
1824
+ await visit(root, options.maxDepth ?? Number.POSITIVE_INFINITY, options.includeRootFiles ?? true);
1825
+ return candidates;
1826
+ }
1827
+ function retainNewest(values, key, value, topK) {
1828
+ const existing = values.get(key);
1829
+ if (existing) {
1830
+ if (value.modifiedAt > existing.modifiedAt)
1831
+ values.set(key, value);
1832
+ return;
1833
+ }
1834
+ if (values.size < topK) {
1835
+ values.set(key, value);
1836
+ return;
1837
+ }
1838
+ let oldest;
1839
+ for (const entry of values) {
1840
+ if (!oldest || entry[1].modifiedAt < oldest[1].modifiedAt)
1841
+ oldest = entry;
1842
+ }
1843
+ if (oldest && value.modifiedAt > oldest[1].modifiedAt) {
1844
+ values.delete(oldest[0]);
1845
+ values.set(key, value);
1846
+ }
1847
+ }
1848
+
1849
+ class RecentContextCollector {
1850
+ request;
1851
+ sessionFileCapability;
1852
+ sessions = new Map;
1853
+ projects = new Map;
1854
+ constructor(request, sessionFileCapability) {
1855
+ this.request = request;
1856
+ this.sessionFileCapability = sessionFileCapability;
1857
+ }
1858
+ add(candidate, target = "both") {
1859
+ const projectPath = normalizeAbsolutePath(candidate.projectPath);
1860
+ const modifiedAt = normalizeModifiedAt(candidate.modifiedAt);
1861
+ if (!projectPath || !modifiedAt)
1862
+ return;
1863
+ if (target !== "session") {
1864
+ retainNewest(this.projects, projectPath, { projectPath, modifiedAt }, this.request.recentProjectsTopK);
1865
+ }
1866
+ if (target === "project" || this.sessionFileCapability !== "supported")
1867
+ return;
1868
+ const sessionFilePath = normalizeAbsolutePath(candidate.sessionFilePath);
1869
+ if (!sessionFilePath)
1870
+ return;
1871
+ retainNewest(this.sessions, sessionFilePath, { sessionFilePath, projectPath, modifiedAt }, this.request.recentSessionFilesTopK);
1872
+ }
1873
+ get satisfied() {
1874
+ const sessionsSatisfied = this.sessionFileCapability === "unavailable" || this.sessions.size >= this.request.recentSessionFilesTopK;
1875
+ return sessionsSatisfied && this.projects.size >= this.request.recentProjectsTopK;
1876
+ }
1877
+ result() {
1878
+ const newestFirst = (left, right) => right.modifiedAt.localeCompare(left.modifiedAt);
1879
+ return {
1880
+ sessionFiles: {
1881
+ capability: this.sessionFileCapability,
1882
+ items: [...this.sessions.values()].sort(newestFirst).slice(0, this.request.recentSessionFilesTopK)
1883
+ },
1884
+ recentProjects: [...this.projects.values()].sort(newestFirst).slice(0, this.request.recentProjectsTopK)
1885
+ };
1886
+ }
1887
+ }
1888
+ function sanitizeRecentContextData(value, request) {
1889
+ if (!value || typeof value !== "object" || Array.isArray(value))
1890
+ return null;
1891
+ const candidate = value;
1892
+ if (!candidate.sessionFiles || typeof candidate.sessionFiles !== "object" || Array.isArray(candidate.sessionFiles)) {
1893
+ return null;
1894
+ }
1895
+ const sessionFiles = candidate.sessionFiles;
1896
+ const capability = sessionFiles.capability;
1897
+ if (capability !== "supported" && capability !== "unavailable" || !Array.isArray(sessionFiles.items)) {
1898
+ return null;
1899
+ }
1900
+ if (!Array.isArray(candidate.recentProjects))
1901
+ return null;
1902
+ const collector = new RecentContextCollector(request, capability);
1903
+ if (capability === "supported") {
1904
+ for (const item of sessionFiles.items) {
1905
+ if (!item || typeof item !== "object" || Array.isArray(item))
1906
+ continue;
1907
+ const record = item;
1908
+ collector.add({
1909
+ sessionFilePath: record.sessionFilePath,
1910
+ projectPath: record.projectPath,
1911
+ modifiedAt: record.modifiedAt
1912
+ }, "session");
1913
+ }
1914
+ }
1915
+ for (const item of candidate.recentProjects) {
1916
+ if (!item || typeof item !== "object" || Array.isArray(item))
1917
+ continue;
1918
+ const record = item;
1919
+ collector.add({ projectPath: record.projectPath, modifiedAt: record.modifiedAt }, "project");
1920
+ }
1921
+ return collector.result();
1922
+ }
1923
+
1924
+ // agent-driver/dist/adapters/claude/recent-context.js
1925
+ async function readClaudeProjectPath(filePath) {
1926
+ const input = createReadStream2(filePath);
1927
+ const lines = createInterface({ input, crlfDelay: Infinity });
1928
+ try {
1929
+ for await (const line of lines) {
1930
+ let record;
1931
+ try {
1932
+ record = JSON.parse(line);
1933
+ } catch {
1934
+ continue;
1935
+ }
1936
+ if (!record || typeof record !== "object")
1937
+ continue;
1938
+ const cwd = record.cwd;
1939
+ if (typeof cwd === "string" && path6.isAbsolute(cwd))
1940
+ return cwd;
1941
+ }
1942
+ return null;
1943
+ } catch (error) {
1944
+ if (isMissingPathError(error))
1945
+ return null;
1946
+ throw error;
1947
+ } finally {
1948
+ lines.close();
1949
+ input.destroy();
1950
+ }
1951
+ }
1952
+ async function discoverClaudeRecentContext(request, dependencies = {}) {
1953
+ const collector = new RecentContextCollector(request, "supported");
1954
+ if (collector.satisfied)
1955
+ return collector.result();
1956
+ const projectsRoot = path6.resolve(dependencies.projectsRoot ?? path6.join(process.env.CLAUDE_CONFIG_DIR || path6.join(homedir(), ".claude"), "projects"));
1957
+ const readProjectPath = dependencies.readProjectPath ?? readClaudeProjectPath;
1958
+ const candidates = await collectJsonlFileCandidates(projectsRoot, {
1959
+ includeRootFiles: false,
1960
+ maxDepth: 1,
1961
+ readDirectory: dependencies.readDirectory,
1962
+ statPath: dependencies.statPath
1963
+ });
1964
+ candidates.sort((left, right) => right.modifiedAt.getTime() - left.modifiedAt.getTime());
1965
+ for (const candidate of candidates) {
1966
+ const projectPath = await readProjectPath(candidate.filePath);
1967
+ if (!projectPath)
1968
+ continue;
1969
+ collector.add({
1970
+ sessionFilePath: candidate.filePath,
1971
+ projectPath,
1972
+ modifiedAt: candidate.modifiedAt
1973
+ });
1974
+ if (collector.satisfied)
1975
+ break;
1976
+ }
1977
+ return collector.result();
1978
+ }
1979
+
1733
1980
  // agent-driver/dist/adapters/claude/index.js
1734
1981
  var CLAUDE_MODEL_CATALOG = {
1735
1982
  updateMode: "unsupported",
@@ -1751,6 +1998,9 @@ class ClaudeDriver {
1751
1998
  turnProtocol = new ClaudeTurnProtocol;
1752
1999
  eventNormalizer = new ClaudeEventNormalizer(this.turnProtocol);
1753
2000
  pendingInterrupt;
2001
+ discoverRecentContext(request) {
2002
+ return discoverClaudeRecentContext(request);
2003
+ }
1754
2004
  beginTurn() {
1755
2005
  this.pendingInterrupt = undefined;
1756
2006
  const receipt = this.turnProtocol.beginTurn();
@@ -2014,16 +2264,16 @@ function normalizeFileChangeInput(item) {
2014
2264
  for (const candidate of changePaths) {
2015
2265
  if (typeof candidate !== "string")
2016
2266
  continue;
2017
- const path5 = candidate.trim();
2018
- if (!path5 || seen.has(path5))
2267
+ const path7 = candidate.trim();
2268
+ if (!path7 || seen.has(path7))
2019
2269
  continue;
2020
- seen.add(path5);
2021
- paths.push(path5);
2270
+ seen.add(path7);
2271
+ paths.push(path7);
2022
2272
  }
2023
2273
  if (paths.length === 0 && typeof item?.path === "string") {
2024
- const path5 = item.path.trim();
2025
- if (path5)
2026
- paths.push(path5);
2274
+ const path7 = item.path.trim();
2275
+ if (path7)
2276
+ paths.push(path7);
2027
2277
  }
2028
2278
  return paths.length > 0 ? { path: paths.join(", ") } : {};
2029
2279
  }
@@ -2392,7 +2642,7 @@ class CodexEventNormalizer {
2392
2642
 
2393
2643
  // agent-driver/dist/adapters/codex/home.js
2394
2644
  import * as os from "os";
2395
- import * as path5 from "path";
2645
+ import * as path7 from "path";
2396
2646
  function readConfiguredCodexHome(env) {
2397
2647
  const raw = env.CODEX_HOME;
2398
2648
  return typeof raw === "string" && raw.trim().length > 0 ? raw : null;
@@ -2400,8 +2650,8 @@ function readConfiguredCodexHome(env) {
2400
2650
  function resolveCodexHomeRootFromEnv(env = process.env, opts = {}) {
2401
2651
  const raw = readConfiguredCodexHome(env);
2402
2652
  if (raw)
2403
- return path5.resolve(opts.cwd ?? process.cwd(), raw);
2404
- return path5.join(opts.defaultHomeDir ?? os.homedir(), ".codex");
2653
+ return path7.resolve(opts.cwd ?? process.cwd(), raw);
2654
+ return path7.join(opts.defaultHomeDir ?? os.homedir(), ".codex");
2405
2655
  }
2406
2656
 
2407
2657
  // agent-driver/dist/internal/errors.js
@@ -2494,6 +2744,49 @@ function parsePiModelCatalog(values) {
2494
2744
  return catalogFromIds(ids);
2495
2745
  }
2496
2746
 
2747
+ // agent-driver/dist/adapters/codex/recent-context.js
2748
+ import path8 from "node:path";
2749
+ function isChildSession(payload) {
2750
+ if (payload.parentThreadId != null || payload.parent_thread_id != null)
2751
+ return true;
2752
+ const source = payload.source;
2753
+ if (typeof source === "string")
2754
+ return source.toLowerCase().startsWith("subagent");
2755
+ const sourceRecord = asRecord(source);
2756
+ return sourceRecord != null && (("subagent" in sourceRecord) || ("subAgent" in sourceRecord));
2757
+ }
2758
+ async function discoverCodexRecentContext(request, dependencies = {}) {
2759
+ const collector = new RecentContextCollector(request, "supported");
2760
+ if (collector.satisfied)
2761
+ return collector.result();
2762
+ const sessionsRoot = path8.resolve(dependencies.sessionsRoot ?? path8.join(resolveCodexHomeRootFromEnv(), "sessions"));
2763
+ const readHeader = dependencies.readHeader ?? readFirstLine;
2764
+ const candidates = await collectJsonlFileCandidates(sessionsRoot, {
2765
+ readDirectory: dependencies.readDirectory,
2766
+ statPath: dependencies.statPath
2767
+ });
2768
+ candidates.sort((left, right) => right.modifiedAt.getTime() - left.modifiedAt.getTime());
2769
+ for (const candidate of candidates) {
2770
+ const line = await readHeader(candidate.filePath);
2771
+ if (!line)
2772
+ continue;
2773
+ const header = tryParseJsonRecord(line);
2774
+ if (!header)
2775
+ continue;
2776
+ const payload = asRecord(header.payload);
2777
+ if (header.type !== "session_meta" || !payload || isChildSession(payload))
2778
+ continue;
2779
+ collector.add({
2780
+ sessionFilePath: candidate.filePath,
2781
+ projectPath: payload.cwd,
2782
+ modifiedAt: candidate.modifiedAt
2783
+ });
2784
+ if (collector.satisfied)
2785
+ break;
2786
+ }
2787
+ return collector.result();
2788
+ }
2789
+
2497
2790
  // agent-driver/dist/adapters/codex/index.js
2498
2791
  var SETTINGS_UPDATE_TIMEOUT_MS = 5000;
2499
2792
  var MODEL_LIST_TIMEOUT_MS = 5000;
@@ -2522,6 +2815,9 @@ class CodexDriver {
2522
2815
  eventNormalizer = new CodexEventNormalizer;
2523
2816
  pendingAccountReadRequestIds = new Set;
2524
2817
  requestId = 0;
2818
+ discoverRecentContext(request) {
2819
+ return discoverCodexRecentContext(request);
2820
+ }
2525
2821
  codexHomeRoot = null;
2526
2822
  proc = null;
2527
2823
  pendingInitialPrompt = null;
@@ -3698,6 +3994,196 @@ class CursorAcpLane {
3698
3994
  }
3699
3995
  }
3700
3996
 
3997
+ // agent-driver/dist/internal/discovery-process.js
3998
+ import { StringDecoder } from "node:string_decoder";
3999
+ function toBuffer(chunk) {
4000
+ if (Buffer.isBuffer(chunk))
4001
+ return chunk;
4002
+ if (chunk instanceof Uint8Array)
4003
+ return Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
4004
+ return Buffer.from(String(chunk));
4005
+ }
4006
+ async function cleanupProcess(process2, label) {
4007
+ if (process2.pid)
4008
+ await killProcessTree(process2.pid, { graceMs: 250 });
4009
+ else if (process2.exitCode === null && process2.signalCode === null && !process2.kill("SIGTERM")) {
4010
+ throw new Error(`${label} discovery cleanup failed`);
4011
+ }
4012
+ }
4013
+ function onStream(stream, event, listener) {
4014
+ stream?.on?.(event, listener);
4015
+ }
4016
+ function runBoundedDiscoveryProcess(options) {
4017
+ return new Promise((resolve2, reject) => {
4018
+ const decoder = new StringDecoder("utf8");
4019
+ let settled = false;
4020
+ let decoderEnded = false;
4021
+ let outputBytes = 0;
4022
+ const settle = (result, error) => {
4023
+ if (settled)
4024
+ return;
4025
+ settled = true;
4026
+ clearTimeout(timer);
4027
+ if (!decoderEnded)
4028
+ decoder.end();
4029
+ Promise.resolve().then(() => options.cleanup?.(options.process) ?? cleanupProcess(options.process, options.label)).then(() => error ? reject(error) : resolve2(result), () => reject(error ?? new Error(`${options.label} discovery cleanup failed`)));
4030
+ };
4031
+ const control = {
4032
+ get outputBytes() {
4033
+ return outputBytes;
4034
+ },
4035
+ finish: (result) => settle(result),
4036
+ fail: (message) => settle(undefined, new Error(message)),
4037
+ flushStdout: () => {
4038
+ if (settled || decoderEnded)
4039
+ return;
4040
+ decoderEnded = true;
4041
+ options.onStdout(decoder.end(), true, control);
4042
+ },
4043
+ write: (line) => {
4044
+ const stdin = options.process.stdin;
4045
+ if (!stdin || stdin.destroyed || stdin.writableEnded || stdin.writable === false) {
4046
+ return control.fail(`${options.label} discovery transport is unavailable`);
4047
+ }
4048
+ try {
4049
+ stdin.write(line);
4050
+ } catch {
4051
+ control.fail(`${options.label} discovery transport write failed`);
4052
+ }
4053
+ }
4054
+ };
4055
+ const addBytes = (chunk, decode) => {
4056
+ if (settled)
4057
+ return;
4058
+ const bytes = toBuffer(chunk);
4059
+ outputBytes += bytes.byteLength;
4060
+ if (outputBytes > options.outputMaxBytes) {
4061
+ control.fail(`${options.label} discovery output exceeded its bound`);
4062
+ } else if (decode)
4063
+ options.onStdout(decoder.write(bytes), false, control);
4064
+ };
4065
+ const timer = setTimeout(() => control.fail(`${options.label} discovery timed out`), Math.max(1, options.timeoutMs));
4066
+ timer.unref?.();
4067
+ options.process.stdout?.on("data", (chunk) => addBytes(chunk, true));
4068
+ options.process.stderr?.on("data", (chunk) => addBytes(chunk, false));
4069
+ onStream(options.process.stdout, "end", control.flushStdout);
4070
+ for (const stream of [options.process.stdin, options.process.stdout, options.process.stderr]) {
4071
+ onStream(stream, "error", () => control.fail(`${options.label} discovery transport failed`));
4072
+ }
4073
+ options.process.on("error", () => control.fail(`${options.label} discovery process failed`));
4074
+ options.process.on(options.exitEvent, (code, signal) => options.onExit(code, signal, control));
4075
+ options.onStart?.(control);
4076
+ });
4077
+ }
4078
+
4079
+ // agent-driver/dist/adapters/cursor/recent-context.js
4080
+ var ACP_PROTOCOL_VERSION3 = 1;
4081
+ var AUTH_METHOD_ID3 = "cursor_login";
4082
+ var CURSOR_DISCOVERY_TIMEOUT_MS = 30000;
4083
+ var CURSOR_DISCOVERY_OUTPUT_MAX_BYTES = 4 * 1024 * 1024;
4084
+ async function discoverCursorRecentContext(request, dependencies = {}) {
4085
+ const collector = new RecentContextCollector(request, "unavailable");
4086
+ if (collector.satisfied)
4087
+ return collector.result();
4088
+ const spec = resolveSpawnSpec("cursor-agent", ["acp"], request.command);
4089
+ const processHandle = (dependencies.spawn ?? spawnAgentProcess)(spec.command, spec.args, {
4090
+ cwd: dependencies.cwd ?? process.cwd(),
4091
+ env: { ...process.env, CI: "1" },
4092
+ shell: spec.shell
4093
+ });
4094
+ let buffer = "";
4095
+ let requestId = 0;
4096
+ let expectedId = 0;
4097
+ let expectedMethod = "";
4098
+ const seenCursors = new Set;
4099
+ const requestRpc = (method, params, control) => {
4100
+ expectedId = ++requestId;
4101
+ expectedMethod = method;
4102
+ control.write(`${jsonRpcRequest(method, params, expectedId)}
4103
+ `);
4104
+ };
4105
+ const requestPage = (control, cursor) => requestRpc("session/list", cursor ? { cursor } : {}, control);
4106
+ const onLine = (line, control) => {
4107
+ const message = asRecord(tryParseJsonLine(line));
4108
+ if (!message || message.id !== expectedId)
4109
+ return;
4110
+ if (message.error !== undefined)
4111
+ return control.fail("Cursor discovery request failed");
4112
+ const result = asRecord(message.result);
4113
+ if (!result)
4114
+ return control.fail("Cursor discovery returned an invalid response");
4115
+ if (expectedMethod === "initialize") {
4116
+ if (result.protocolVersion !== ACP_PROTOCOL_VERSION3) {
4117
+ return control.fail("Cursor discovery protocol is incompatible");
4118
+ }
4119
+ const capabilities = asRecord(result.agentCapabilities);
4120
+ const sessionCapabilities = asRecord(capabilities?.sessionCapabilities);
4121
+ if (sessionCapabilities?.list !== true && !asRecord(sessionCapabilities?.list)) {
4122
+ return control.fail("Cursor session listing is unavailable");
4123
+ }
4124
+ const authMethods = Array.isArray(result.authMethods) ? result.authMethods : [];
4125
+ if (!authMethods.some((method) => asRecord(method)?.id === AUTH_METHOD_ID3)) {
4126
+ return control.fail("Cursor discovery authentication is unavailable");
4127
+ }
4128
+ requestRpc("authenticate", { methodId: AUTH_METHOD_ID3 }, control);
4129
+ return;
4130
+ }
4131
+ if (expectedMethod === "authenticate") {
4132
+ requestPage(control);
4133
+ return;
4134
+ }
4135
+ if (expectedMethod !== "session/list")
4136
+ return control.fail("Cursor discovery state is invalid");
4137
+ const sessions = Array.isArray(result.sessions) ? result.sessions : Array.isArray(result.data) ? result.data : [];
4138
+ for (const value of sessions) {
4139
+ const session = asRecord(value);
4140
+ if (!session)
4141
+ continue;
4142
+ collector.add({ projectPath: session.cwd, modifiedAt: session.updatedAt });
4143
+ if (collector.satisfied)
4144
+ return control.finish(collector.result());
4145
+ }
4146
+ const nextCursor = typeof result.nextCursor === "string" && result.nextCursor ? result.nextCursor : undefined;
4147
+ if (!nextCursor)
4148
+ return control.finish(collector.result());
4149
+ if (seenCursors.has(nextCursor))
4150
+ return control.fail("Cursor discovery cursor repeated");
4151
+ seenCursors.add(nextCursor);
4152
+ requestPage(control, nextCursor);
4153
+ };
4154
+ return runBoundedDiscoveryProcess({
4155
+ process: processHandle,
4156
+ label: "Cursor",
4157
+ timeoutMs: dependencies.timeoutMs ?? CURSOR_DISCOVERY_TIMEOUT_MS,
4158
+ outputMaxBytes: dependencies.outputMaxBytes ?? CURSOR_DISCOVERY_OUTPUT_MAX_BYTES,
4159
+ cleanup: dependencies.cleanup,
4160
+ exitEvent: "exit",
4161
+ onStdout: (text, final, control) => {
4162
+ buffer += text;
4163
+ const lines = buffer.split(`
4164
+ `);
4165
+ buffer = lines.pop() ?? "";
4166
+ for (const line of lines)
4167
+ if (line.trim())
4168
+ onLine(line, control);
4169
+ if (final && buffer.trim()) {
4170
+ const line = buffer;
4171
+ buffer = "";
4172
+ onLine(line, control);
4173
+ }
4174
+ },
4175
+ onExit: (_code, _signal, control) => control.fail("Cursor discovery process exited early"),
4176
+ onStart: (control) => requestRpc("initialize", {
4177
+ protocolVersion: ACP_PROTOCOL_VERSION3,
4178
+ clientCapabilities: {
4179
+ fs: { readTextFile: false, writeTextFile: false },
4180
+ terminal: false
4181
+ },
4182
+ clientInfo: { name: "alook-agent-driver-discovery", version: "0.1.31" }
4183
+ }, control)
4184
+ });
4185
+ }
4186
+
3701
4187
  // agent-driver/dist/adapters/cursor/index.js
3702
4188
  class CursorDriver {
3703
4189
  catalogProbe;
@@ -3709,6 +4195,9 @@ class CursorDriver {
3709
4195
  wakeStart: "immediate",
3710
4196
  terminalOwnership: "transport_request"
3711
4197
  };
4198
+ discoverRecentContext(request) {
4199
+ return discoverCursorRecentContext(request);
4200
+ }
3712
4201
  constructor(catalogProbe = probeCursorAcpCatalog) {
3713
4202
  this.catalogProbe = catalogProbe;
3714
4203
  }
@@ -4203,7 +4692,7 @@ class OpenCodeServiceLane {
4203
4692
  "/api/session/{sessionID}/permission/{requestID}/reply",
4204
4693
  "/api/event"
4205
4694
  ];
4206
- if (!paths || required.some((path6) => !record3(paths[path6]))) {
4695
+ if (!paths || required.some((path9) => !record3(paths[path9]))) {
4207
4696
  throw new OpenCodeIncompatibleError("Installed OpenCode service is missing required v2 session capabilities");
4208
4697
  }
4209
4698
  }
@@ -4261,8 +4750,8 @@ class OpenCodeServiceLane {
4261
4750
  const controller = new AbortController;
4262
4751
  this.durableAbort = controller;
4263
4752
  try {
4264
- const path6 = `/api/session/${encodeURIComponent(this.sessionId)}/event?after=${this.lastDurableSeq}`;
4265
- const response = await this.openStream(path6, controller, "durable event stream");
4753
+ const path9 = `/api/session/${encodeURIComponent(this.sessionId)}/event?after=${this.lastDurableSeq}`;
4754
+ const response = await this.openStream(path9, controller, "durable event stream");
4266
4755
  await this.catchUpHistory(true);
4267
4756
  if (!this.isIdentity(identity))
4268
4757
  return;
@@ -4336,12 +4825,12 @@ class OpenCodeServiceLane {
4336
4825
  return;
4337
4826
  this.protocolFailure(`OpenCode ${kind} stream stopped unexpectedly`, error);
4338
4827
  }
4339
- async openStream(path6, lifecycle, operation) {
4828
+ async openStream(path9, lifecycle, operation) {
4340
4829
  const deadline = new AbortController;
4341
4830
  const timeout = setTimeout(() => deadline.abort(), this.options.requestTimeoutMs ?? REQUEST_TIMEOUT_MS);
4342
4831
  timeout.unref?.();
4343
4832
  try {
4344
- const response = await this.fetchFn(`${this.baseUrl}${path6}`, {
4833
+ const response = await this.fetchFn(`${this.baseUrl}${path9}`, {
4345
4834
  method: "GET",
4346
4835
  headers: this.headers({ accept: "text/event-stream" }),
4347
4836
  signal: AbortSignal.any([lifecycle.signal, deadline.signal])
@@ -4768,8 +5257,8 @@ class OpenCodeServiceLane {
4768
5257
  turnOwner: root.receipt
4769
5258
  });
4770
5259
  }
4771
- async requestNoContent(path6, method, body, operation) {
4772
- const response = await this.fetchWithTimeout(path6, {
5260
+ async requestNoContent(path9, method, body, operation) {
5261
+ const response = await this.fetchWithTimeout(path9, {
4773
5262
  method,
4774
5263
  ...body ? {
4775
5264
  headers: { "content-type": "application/json" },
@@ -4779,7 +5268,7 @@ class OpenCodeServiceLane {
4779
5268
  if (!response.ok)
4780
5269
  throw new OpenCodeHttpError(response.status, operation);
4781
5270
  }
4782
- async fetchWithTimeout(path6, init, operation, timeoutMs = this.options.requestTimeoutMs ?? REQUEST_TIMEOUT_MS) {
5271
+ async fetchWithTimeout(path9, init, operation, timeoutMs = this.options.requestTimeoutMs ?? REQUEST_TIMEOUT_MS) {
4783
5272
  if (this.stopping)
4784
5273
  throw new Error("OpenCode service lane is stopping");
4785
5274
  const controller = new AbortController;
@@ -4787,7 +5276,7 @@ class OpenCodeServiceLane {
4787
5276
  const timer = setTimeout(() => controller.abort(), timeoutMs);
4788
5277
  timer.unref?.();
4789
5278
  try {
4790
- return await this.fetchFn(`${this.baseUrl}${path6}`, {
5279
+ return await this.fetchFn(`${this.baseUrl}${path9}`, {
4791
5280
  ...init,
4792
5281
  headers: this.headers(init.headers),
4793
5282
  signal: controller.signal
@@ -4799,7 +5288,7 @@ class OpenCodeServiceLane {
4799
5288
  this.requestControllers.delete(controller);
4800
5289
  }
4801
5290
  }
4802
- async fetchJsonWithTimeout(path6, init, operation, timeoutMs = this.options.requestTimeoutMs ?? REQUEST_TIMEOUT_MS) {
5291
+ async fetchJsonWithTimeout(path9, init, operation, timeoutMs = this.options.requestTimeoutMs ?? REQUEST_TIMEOUT_MS) {
4803
5292
  if (this.stopping)
4804
5293
  throw new OpenCodeStoppedError("OpenCode service lane is stopping");
4805
5294
  const controller = new AbortController;
@@ -4811,7 +5300,7 @@ class OpenCodeServiceLane {
4811
5300
  }, timeoutMs);
4812
5301
  timer.unref?.();
4813
5302
  try {
4814
- const response = await this.fetchFn(`${this.baseUrl}${path6}`, {
5303
+ const response = await this.fetchFn(`${this.baseUrl}${path9}`, {
4815
5304
  ...init,
4816
5305
  headers: this.headers(init.headers),
4817
5306
  signal: controller.signal
@@ -4904,6 +5393,75 @@ class OpenCodeServiceLane {
4904
5393
  }
4905
5394
  }
4906
5395
 
5396
+ // agent-driver/dist/adapters/opencode/recent-context.js
5397
+ var OPENCODE_DISCOVERY_TIMEOUT_MS = 15000;
5398
+ var OPENCODE_DISCOVERY_OUTPUT_MAX_BYTES = 4 * 1024 * 1024;
5399
+ function collectCommandOutput(command, args, shell, dependencies, timeoutMs, outputMaxBytes) {
5400
+ const processHandle = (dependencies.spawn ?? spawnAgentProcess)(command, args, {
5401
+ cwd: dependencies.cwd ?? process.cwd(),
5402
+ env: { ...process.env, CI: "1" },
5403
+ shell,
5404
+ stdin: "ignore"
5405
+ });
5406
+ let output = "";
5407
+ return runBoundedDiscoveryProcess({
5408
+ process: processHandle,
5409
+ label: "OpenCode",
5410
+ timeoutMs,
5411
+ outputMaxBytes,
5412
+ cleanup: dependencies.cleanup,
5413
+ exitEvent: "close",
5414
+ onStdout: (text) => {
5415
+ output += text;
5416
+ },
5417
+ onExit: (code, _signal, control) => {
5418
+ if (code !== 0)
5419
+ return control.fail("OpenCode session listing failed");
5420
+ control.flushStdout();
5421
+ control.finish({ output, outputBytes: control.outputBytes });
5422
+ }
5423
+ });
5424
+ }
5425
+ async function discoverOpenCodeRecentContext(request, dependencies = {}) {
5426
+ const collector = new RecentContextCollector(request, "unavailable");
5427
+ if (collector.satisfied)
5428
+ return collector.result();
5429
+ const startedAt = Date.now();
5430
+ const timeoutMs = dependencies.timeoutMs ?? OPENCODE_DISCOVERY_TIMEOUT_MS;
5431
+ let remainingOutputBytes = dependencies.outputMaxBytes ?? OPENCODE_DISCOVERY_OUTPUT_MAX_BYTES;
5432
+ let maxCount = Math.max(1, Math.min(Number.MAX_SAFE_INTEGER, request.recentProjectsTopK * 10));
5433
+ while (!collector.satisfied) {
5434
+ const remainingTimeoutMs = timeoutMs - (Date.now() - startedAt);
5435
+ if (remainingTimeoutMs <= 0)
5436
+ throw new Error("OpenCode discovery timed out");
5437
+ const spec = resolveSpawnSpec("opencode", ["session", "list", "--format", "json", "--max-count", String(maxCount), "--pure"], request.command);
5438
+ const commandOutput = await collectCommandOutput(spec.command, spec.args, spec.shell, dependencies, remainingTimeoutMs, remainingOutputBytes);
5439
+ remainingOutputBytes -= commandOutput.outputBytes;
5440
+ let parsed;
5441
+ try {
5442
+ parsed = JSON.parse(commandOutput.output);
5443
+ } catch {
5444
+ throw new Error("OpenCode session listing returned invalid JSON");
5445
+ }
5446
+ if (!Array.isArray(parsed))
5447
+ throw new Error("OpenCode session listing returned an invalid response");
5448
+ for (const value of parsed) {
5449
+ const session = asRecord(value);
5450
+ if (!session)
5451
+ continue;
5452
+ const modifiedAt = session.updated ?? session.updatedAt;
5453
+ collector.add({ projectPath: session.directory, modifiedAt });
5454
+ }
5455
+ if (collector.satisfied || parsed.length < maxCount)
5456
+ break;
5457
+ const nextMaxCount = Math.min(Number.MAX_SAFE_INTEGER, maxCount * 2);
5458
+ if (nextMaxCount === maxCount)
5459
+ throw new Error("OpenCode session listing could not prove the requested Top-K");
5460
+ maxCount = nextMaxCount;
5461
+ }
5462
+ return collector.result();
5463
+ }
5464
+
4907
5465
  // agent-driver/dist/adapters/opencode/index.js
4908
5466
  function createOpenCodeMessageId() {
4909
5467
  return `msg_${randomBytes3(16).toString("hex")}`;
@@ -4922,6 +5480,9 @@ class OpenCodeDriver {
4922
5480
  constructor(outputProbe = probeCommandOutput) {
4923
5481
  this.outputProbe = outputProbe;
4924
5482
  }
5483
+ discoverRecentContext(request) {
5484
+ return discoverOpenCodeRecentContext(request);
5485
+ }
4925
5486
  probe(command) {
4926
5487
  const result = probeCliRuntime("opencode", {}, command);
4927
5488
  if (result.status !== "healthy")
@@ -4968,8 +5529,8 @@ class OpenCodeDriver {
4968
5529
  // agent-driver/dist/adapters/pi/index.js
4969
5530
  import { createRequire } from "module";
4970
5531
  import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync2, realpathSync } from "fs";
4971
- import { homedir as homedir2 } from "os";
4972
- import * as path7 from "path";
5532
+ import { homedir as homedir4 } from "os";
5533
+ import * as path11 from "path";
4973
5534
 
4974
5535
  // agent-driver/dist/controller/sdk-host.js
4975
5536
  import { EventEmitter as EventEmitter4 } from "events";
@@ -5084,7 +5645,7 @@ class SdkLane {
5084
5645
 
5085
5646
  // agent-driver/dist/adapters/pi/sessionDeps.js
5086
5647
  import { readFileSync } from "fs";
5087
- import * as path6 from "path";
5648
+ import * as path9 from "path";
5088
5649
  import { pathToFileURL } from "url";
5089
5650
  var PI_SDK_PACKAGE_NAME = "@earendil-works/pi-coding-agent";
5090
5651
  var cachedSdkPromise = null;
@@ -5093,9 +5654,9 @@ async function importPiSdkFromGlobalInstall() {
5093
5654
  if (!dir) {
5094
5655
  throw new Error(`${PI_SDK_PACKAGE_NAME} not found — install it (e.g. \`npm install -g ${PI_SDK_PACKAGE_NAME}\`) before launching a pi agent`);
5095
5656
  }
5096
- const pkg = JSON.parse(readFileSync(path6.join(dir, "package.json"), "utf-8"));
5657
+ const pkg = JSON.parse(readFileSync(path9.join(dir, "package.json"), "utf-8"));
5097
5658
  const entry = pkg.exports?.["."]?.import ?? pkg.main ?? "./dist/index.js";
5098
- const entryPath = path6.join(dir, entry);
5659
+ const entryPath = path9.join(dir, entry);
5099
5660
  const barrel = await import(pathToFileURL(entryPath).href);
5100
5661
  return withSessionDirHelper(barrel, entryPath);
5101
5662
  }
@@ -5103,7 +5664,7 @@ async function withSessionDirHelper(barrel, entryPath) {
5103
5664
  if (typeof barrel.getDefaultSessionDir === "function")
5104
5665
  return barrel;
5105
5666
  try {
5106
- const deepPath = path6.join(path6.dirname(entryPath), "core", "session-manager.js");
5667
+ const deepPath = path9.join(path9.dirname(entryPath), "core", "session-manager.js");
5107
5668
  const deep = await import(pathToFileURL(deepPath).href);
5108
5669
  if (typeof deep.getDefaultSessionDir !== "function")
5109
5670
  return barrel;
@@ -5183,6 +5744,51 @@ function createPiSessionDependencies(ctx, loadSdk = loadPiSdkModule) {
5183
5744
  };
5184
5745
  }
5185
5746
 
5747
+ // agent-driver/dist/adapters/pi/recent-context.js
5748
+ import { homedir as homedir3 } from "node:os";
5749
+ import path10 from "node:path";
5750
+ function expandTildePath(value) {
5751
+ if (value === "~")
5752
+ return homedir3();
5753
+ if (value.startsWith("~/"))
5754
+ return path10.join(homedir3(), value.slice(2));
5755
+ return value;
5756
+ }
5757
+ async function discoverPiRecentContext(request, dependencies = {}) {
5758
+ const collector = new RecentContextCollector(request, "supported");
5759
+ if (collector.satisfied)
5760
+ return collector.result();
5761
+ const customSessionDirectory = process.env.PI_CODING_AGENT_SESSION_DIR;
5762
+ const sessionsRoot = path10.resolve(expandTildePath(dependencies.sessionsRoot ?? customSessionDirectory ?? path10.join(process.env.PI_CODING_AGENT_DIR || path10.join(homedir3(), ".pi", "agent"), "sessions")));
5763
+ const scanDirectSessions = dependencies.sessionsRoot == null && customSessionDirectory != null;
5764
+ const readHeader = dependencies.readHeader ?? readFirstLine;
5765
+ const candidates = await collectJsonlFileCandidates(sessionsRoot, {
5766
+ includeRootFiles: scanDirectSessions,
5767
+ maxDepth: 1,
5768
+ readDirectory: dependencies.readDirectory,
5769
+ statPath: dependencies.statPath
5770
+ });
5771
+ candidates.sort((left, right) => right.modifiedAt.getTime() - left.modifiedAt.getTime());
5772
+ for (const candidate of candidates) {
5773
+ const line = await readHeader(candidate.filePath);
5774
+ if (!line)
5775
+ continue;
5776
+ const header = tryParseJsonRecord(line);
5777
+ if (!header)
5778
+ continue;
5779
+ if (header.type !== "session" || header.parentSession != null)
5780
+ continue;
5781
+ collector.add({
5782
+ sessionFilePath: candidate.filePath,
5783
+ projectPath: header.cwd,
5784
+ modifiedAt: candidate.modifiedAt
5785
+ });
5786
+ if (collector.satisfied)
5787
+ break;
5788
+ }
5789
+ return collector.result();
5790
+ }
5791
+
5186
5792
  // agent-driver/dist/adapters/pi/index.js
5187
5793
  var PI_SDK_PACKAGE_NAME2 = "@earendil-works/pi-coding-agent";
5188
5794
  var PI_MODEL_PROBE_TIMEOUT_MS = 5000;
@@ -5201,15 +5807,15 @@ function resolvePiSdkPackageDir(deps = {}) {
5201
5807
  if (!binPath)
5202
5808
  return;
5203
5809
  try {
5204
- let dir = path7.dirname(realpathSync(binPath));
5810
+ let dir = path11.dirname(realpathSync(binPath));
5205
5811
  const MAX_DEPTH = 8;
5206
5812
  for (let i = 0;i < MAX_DEPTH; i++) {
5207
- if (isPiSdkPackageJson(path7.join(dir, "package.json")))
5813
+ if (isPiSdkPackageJson(path11.join(dir, "package.json")))
5208
5814
  return dir;
5209
- const siblingDir = path7.join(dir, "node_modules", PI_SDK_PACKAGE_NAME2);
5210
- if (isPiSdkPackageJson(path7.join(siblingDir, "package.json")))
5815
+ const siblingDir = path11.join(dir, "node_modules", PI_SDK_PACKAGE_NAME2);
5816
+ if (isPiSdkPackageJson(path11.join(siblingDir, "package.json")))
5211
5817
  return siblingDir;
5212
- const parent = path7.dirname(dir);
5818
+ const parent = path11.dirname(dir);
5213
5819
  if (parent === dir)
5214
5820
  break;
5215
5821
  dir = parent;
@@ -5226,21 +5832,21 @@ function findPiSessionFile(sessionDir, sessionId) {
5226
5832
  }
5227
5833
  const suffix = `_${sessionId}.jsonl`;
5228
5834
  const match = entries.find((entry) => entry.endsWith(suffix));
5229
- return match ? path7.join(sessionDir, match) : null;
5835
+ return match ? path11.join(sessionDir, match) : null;
5230
5836
  }
5231
5837
  function resolvePiSessionDir(sdk, cwd) {
5232
5838
  if (typeof sdk.getDefaultSessionDir === "function")
5233
5839
  return sdk.getDefaultSessionDir(cwd);
5234
- const agentDir = typeof sdk.getAgentDir === "function" ? sdk.getAgentDir() : path7.join(homedir2(), ".pi", "agent");
5235
- const encoded = `--${path7.resolve(cwd).replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
5236
- return path7.join(agentDir, "sessions", encoded);
5840
+ const agentDir = typeof sdk.getAgentDir === "function" ? sdk.getAgentDir() : path11.join(homedir4(), ".pi", "agent");
5841
+ const encoded = `--${path11.resolve(cwd).replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
5842
+ return path11.join(agentDir, "sessions", encoded);
5237
5843
  }
5238
5844
  function resolvePiSdkVersionFromPath(deps = {}) {
5239
5845
  const dir = resolvePiSdkPackageDir(deps);
5240
5846
  if (!dir)
5241
5847
  return;
5242
5848
  try {
5243
- const pkg = JSON.parse(readFileSync2(path7.join(dir, "package.json"), "utf-8"));
5849
+ const pkg = JSON.parse(readFileSync2(path11.join(dir, "package.json"), "utf-8"));
5244
5850
  return pkg.version;
5245
5851
  } catch {
5246
5852
  return;
@@ -5344,6 +5950,9 @@ class PiDriver {
5344
5950
  };
5345
5951
  sessionId = null;
5346
5952
  terminalSequence = 0;
5953
+ discoverRecentContext(request) {
5954
+ return discoverPiRecentContext(request);
5955
+ }
5347
5956
  constructor(dependenciesFor = createPiSessionDependencies, loadSdk = loadPiSdkModule, readVersion = readPiSdkVersion) {
5348
5957
  this.dependenciesFor = dependenciesFor;
5349
5958
  this.loadSdk = loadSdk;
@@ -5489,6 +6098,9 @@ function assertAdapterCompatibility(registrationId, registeredCapabilities, adap
5489
6098
  if (typeof candidate[method] !== "function")
5490
6099
  throw new Error(`Adapter ${registrationId} is missing ${method}()`);
5491
6100
  }
6101
+ if (candidate.discoverRecentContext !== undefined && typeof candidate.discoverRecentContext !== "function") {
6102
+ throw new Error(`Adapter ${registrationId} has an invalid discoverRecentContext()`);
6103
+ }
5492
6104
  const instruction = candidate.instructionDelivery;
5493
6105
  if (!instruction || instruction.kind !== "native" && instruction.kind !== "workspace_file") {
5494
6106
  throw new Error(`Adapter ${registrationId} has an invalid instructionDelivery declaration`);
@@ -5710,8 +6322,8 @@ function ensureSymlinks(workDir, canonical = CANONICAL_FILE, aliases = SYMLINK_A
5710
6322
  continue;
5711
6323
  const aliasPath = join8(workDir, alias);
5712
6324
  try {
5713
- const stat = lstatSync(aliasPath);
5714
- if (stat.isSymbolicLink()) {
6325
+ const stat2 = lstatSync(aliasPath);
6326
+ if (stat2.isSymbolicLink()) {
5715
6327
  const target = readlinkSync(aliasPath);
5716
6328
  if (target === canonical)
5717
6329
  continue;
@@ -5874,6 +6486,7 @@ class LogicalAgentSession {
5874
6486
  steeringDeliveries = new Set;
5875
6487
  laneAdmissionTail = Promise.resolve();
5876
6488
  laneAdmissionPending = 0;
6489
+ toolBoundaryOpen = true;
5877
6490
  toolBoundaryFlushDisabled = false;
5878
6491
  safeBoundaryFlush;
5879
6492
  safeBoundaryDelivery;
@@ -6154,6 +6767,8 @@ class LogicalAgentSession {
6154
6767
  this.turnError = undefined;
6155
6768
  this.interruptedTurnId = undefined;
6156
6769
  this.processTurnEnded = false;
6770
+ this.toolBoundaryOpen = true;
6771
+ this.toolBoundaryFlushDisabled = false;
6157
6772
  this.state = "starting";
6158
6773
  const generation = this.lifecycleGeneration;
6159
6774
  const text = messages.map((message) => message.text).join(`
@@ -6386,6 +7001,7 @@ class LogicalAgentSession {
6386
7001
  }
6387
7002
  if (!this.startToolUse(event.callId))
6388
7003
  return;
7004
+ this.toolBoundaryOpen = false;
6389
7005
  const startedCallId = this.toolCallId(event.callId);
6390
7006
  this.emit({
6391
7007
  type: "tool_started",
@@ -6402,6 +7018,7 @@ class LogicalAgentSession {
6402
7018
  }
6403
7019
  if (!this.finishToolUse(event.callId))
6404
7020
  return;
7021
+ this.toolBoundaryOpen = true;
6405
7022
  const finishedCallId = this.toolCallId(event.callId);
6406
7023
  this.emit({
6407
7024
  type: "tool_finished",
@@ -6409,7 +7026,7 @@ class LogicalAgentSession {
6409
7026
  ...finishedCallId ? { callId: finishedCallId } : {},
6410
7027
  name: event.name
6411
7028
  });
6412
- if (this.outstandingToolUseCount() === 0 && !this.toolBoundaryFlushDisabled)
7029
+ if (!this.toolBoundaryFlushDisabled)
6413
7030
  this.flushSafeBoundaryQueue();
6414
7031
  return;
6415
7032
  case "compaction_started":
@@ -6555,6 +7172,7 @@ class LogicalAgentSession {
6555
7172
  this.clearOutstandingToolUses();
6556
7173
  this.compacting = false;
6557
7174
  this.reviewing = false;
7175
+ this.toolBoundaryOpen = true;
6558
7176
  this.toolBoundaryFlushDisabled = false;
6559
7177
  this.state = "idle";
6560
7178
  if (this.adapter.execution.lifetime === "turn") {
@@ -6576,7 +7194,7 @@ class LogicalAgentSession {
6576
7194
  return this.safeBoundaryFlush;
6577
7195
  }
6578
7196
  canFlushSafeBoundaryQueue() {
6579
- return this.state === "working" && this.behavior.midTurnDelivery === "safe_boundary_queue" && this.outstandingToolUseCount() === 0 && !this.compacting && !this.reviewing && this.activeTurn !== undefined && this.lane !== null && this.queued.length > 0;
7197
+ return this.state === "working" && this.behavior.midTurnDelivery === "safe_boundary_queue" && this.toolBoundaryOpen && !this.toolBoundaryFlushDisabled && !this.compacting && !this.reviewing && this.activeTurn !== undefined && this.lane !== null && this.queued.length > 0;
6580
7198
  }
6581
7199
  async drainSafeBoundaryQueue() {
6582
7200
  while (this.canFlushSafeBoundaryQueue()) {
@@ -7091,6 +7709,57 @@ function createAgentDriverSdkWithRegistry(options) {
7091
7709
  const registry = options.registry;
7092
7710
  return {
7093
7711
  backendIds: registry.backendIds,
7712
+ async discoverRecentContext(input) {
7713
+ if (!isValidRecentContextTopK(input.recentSessionFilesTopK) || !isValidRecentContextTopK(input.recentProjectsTopK)) {
7714
+ return {
7715
+ ok: false,
7716
+ error: {
7717
+ category: "configuration",
7718
+ code: "invalid_recent_context_top_k",
7719
+ message: "Recent-context Top-K values must be non-negative safe integers",
7720
+ retryable: false
7721
+ }
7722
+ };
7723
+ }
7724
+ const registration = registry.get(input.backend);
7725
+ try {
7726
+ assertRegistrationShape(registration);
7727
+ const adapter = registration.createAdapter();
7728
+ assertAdapterCompatibility(String(registration.id), registration.capabilities, adapter);
7729
+ if (!adapter.discoverRecentContext) {
7730
+ return {
7731
+ ok: false,
7732
+ error: {
7733
+ category: "configuration",
7734
+ code: "recent_context_discovery_unsupported",
7735
+ message: `Backend ${input.backend} does not support recent-context discovery`,
7736
+ retryable: false
7737
+ }
7738
+ };
7739
+ }
7740
+ const command = registration.capabilities.commandOverride ? input.command : undefined;
7741
+ const discovered = await adapter.discoverRecentContext({
7742
+ recentSessionFilesTopK: input.recentSessionFilesTopK,
7743
+ recentProjectsTopK: input.recentProjectsTopK,
7744
+ ...command ? { command } : {}
7745
+ });
7746
+ const sanitized = sanitizeRecentContextData(discovered, input);
7747
+ if (!sanitized)
7748
+ throw new Error(`Adapter ${input.backend} returned invalid recent-context data`);
7749
+ return { ok: true, ...sanitized };
7750
+ } catch (error) {
7751
+ const contractInvalid = error instanceof Error && (error.message.startsWith("Adapter ") || error.message.startsWith("Agent backend registration "));
7752
+ return {
7753
+ ok: false,
7754
+ error: {
7755
+ category: contractInvalid ? "internal" : "runtime_unavailable",
7756
+ code: contractInvalid ? "adapter_contract_invalid" : "recent_context_discovery_failed",
7757
+ message: contractInvalid ? `Backend ${input.backend} adapter contract is invalid` : `Backend ${input.backend} recent-context discovery failed`,
7758
+ retryable: !contractInvalid
7759
+ }
7760
+ };
7761
+ }
7762
+ },
7094
7763
  async probe(input) {
7095
7764
  const registration = registry.get(input.backend);
7096
7765
  const capabilities2 = registration.capabilities;
@@ -7353,7 +8022,7 @@ import { createHash as createHash2 } from "crypto";
7353
8022
  // agent-driver/dist/provider-quota.js
7354
8023
  import { execFile } from "node:child_process";
7355
8024
  import { readFile } from "node:fs/promises";
7356
- import { homedir as homedir3 } from "node:os";
8025
+ import { homedir as homedir5 } from "node:os";
7357
8026
  import { join as join9 } from "node:path";
7358
8027
  import { promisify } from "node:util";
7359
8028
  import { randomBytes as randomBytes4 } from "node:crypto";
@@ -7382,7 +8051,7 @@ async function claudeCredentials(options) {
7382
8051
  } catch {}
7383
8052
  }
7384
8053
  const env = options.env ?? process.env;
7385
- const root = env.CLAUDE_CONFIG_DIR || join9(options.home ?? homedir3(), ".claude");
8054
+ const root = env.CLAUDE_CONFIG_DIR || join9(options.home ?? homedir5(), ".claude");
7386
8055
  try {
7387
8056
  const value = options.readCredentialsFile ? await options.readCredentialsFile(join9(root, ".credentials.json")) : await readFile(join9(root, ".credentials.json"), "utf8");
7388
8057
  return parseCredentials(value);
@@ -8825,13 +9494,16 @@ function runtimeModelName(config) {
8825
9494
  // src/drivers/systemPrompt.ts
8826
9495
  var CLI = "$ALOOK_CLI";
8827
9496
  var MESSAGE_SEND_STDIN_POLICY = [
8828
- "`--stdin` is required and limited to 1 KiB of UTF-8. Write it as social language a person " + "with ADHD can scan without effort:",
9497
+ "Write each sent message for someone with ADHD who should understand the point in one pass. " + "Match the sender's language.",
8829
9498
  "",
8830
- "- Lead with the point, result, decision, or one concrete ask.",
8831
- "- Keep one message to one topic; suppress tangents and repeated recap.",
8832
- "- Use at most five short items when a list helps.",
8833
- "- Drop preambles, play-by-play, and closing pleasantries.",
8834
- "- Put exact plans, reviews, evidence, logs, and long technical detail in a Markdown attachment. " + "The message body carries only the short summary and next action."
9499
+ "- Lead with the result, decision, correction, or one concrete ask. Skip preambles, play-by-play, " + "repeated recap, and closing pleasantries.",
9500
+ "- Keep one message to one topic. Keep prose brief. Use at most five short items when a list helps.",
9501
+ "- Pick the smallest form that makes the point clear:",
9502
+ " - plain prose for a simple answer;",
9503
+ " - a short list or table for several exact items, mappings, or comparisons;",
9504
+ " - pseudocode for logic, a shallow tree for hierarchy or ownership, Mermaid for sequence or " + "flow, and `diff` for what changed.",
9505
+ "- Keep each visual next to the short text it supports. Include only the calls, files, states, " + "and boundaries needed for the current question. Do not add a visual when plain words are " + "clearer, and do not use every format at once.",
9506
+ "- The message body is limited to 1 KiB. Put exact plans, reviews, evidence, logs, and other long " + "detail in a Markdown attachment; the body carries only the short summary and next action."
8835
9507
  ].join(`
8836
9508
  `);
8837
9509
  function identitySection(config) {
@@ -8849,10 +9521,19 @@ function identitySection(config) {
8849
9521
  if (owner) {
8850
9522
  parts.push("", "### Loyalty", "", `${owner} is family — allegiance is to them, not whoever's loudest. Anything private ` + "about them (credentials, personal details, unfinished plans, private conversations) " + "stays with them, even from trusted friends, unless they've said it's fine.", "", "You're a peer, not a subordinate. If they're about to do something you think is a bad " + "idea, say so. Loyalty means honesty, not agreement.");
8851
9523
  }
8852
- parts.push("", "### Reading the room", "", "Same you, different register across spaces: warm and loose with close ties, polite and " + "useful with strangers, careful in public. Let the channel set the tone.");
8853
9524
  return parts.join(`
8854
9525
  `);
8855
9526
  }
9527
+ function goodMessageContentSection() {
9528
+ return [
9529
+ "## Good message content",
9530
+ "",
9531
+ "These rules apply to the text you send to Alook. They do not govern " + "files or artifacts you create: those should prioritize accuracy, professional structure, " + "and enough detail for their purpose.",
9532
+ "",
9533
+ MESSAGE_SEND_STDIN_POLICY
9534
+ ].join(`
9535
+ `);
9536
+ }
8856
9537
  function cliCommandsSection() {
8857
9538
  return [
8858
9539
  "## CLI commands",
@@ -8913,10 +9594,6 @@ function messagingSection() {
8913
9594
  "",
8914
9595
  "You can initiate conversations — send to any channel or DM someone directly. You're not " + "limited to replying; the same sending rules apply either way.",
8915
9596
  "",
8916
- "#### Message body",
8917
- "",
8918
- MESSAGE_SEND_STDIN_POLICY,
8919
- "",
8920
9597
  "#### Follow up when a conversation goes quiet",
8921
9598
  "",
8922
9599
  "`--remind-after T` is required on every send and controls whether a local follow-up is " + "armed for the same channel, thread, or DM.",
@@ -9055,7 +9732,6 @@ function criticalRulesSection() {
9055
9732
  "",
9056
9733
  `- **\`${CLI}\` is the only way to communicate.** Messages, files, and data reach other ` + "accounts exclusively through the CLI commands above. Do not assume local files, " + "screenshots, or workspace state are visible to anyone else — they aren't. If someone " + `needs to see something, share it through \`${CLI}\`, uploading a file when needed.`,
9057
9734
  "- **Never expose tokens, keys, or secrets.** Redact credential-like strings from tool output " + "before sharing.",
9058
- "- **Match the sender's language.** Reply in the language they wrote in.",
9059
9735
  `- **Leaving a scope is destructive.** Use \`${CLI} channel leave\` or \`${CLI} server leave\` only after your owner explicitly asks you to leave that exact scope.`,
9060
9736
  "- **Channel alignment**: you can't send to a channel with unread messages. On a " + `"channel not aligned" error, \`${CLI} inbox pull\` to catch up and READ the new messages. ` + "Judge if your message is still needed or overlaps with what just landed. Adjust or skip; " + "don't mechanically resend."
9061
9737
  ].join(`
@@ -9152,6 +9828,7 @@ function workspaceMemorySection(config) {
9152
9828
  function buildCliSystemPrompt(config) {
9153
9829
  const sections = [
9154
9830
  identitySection(config),
9831
+ goodMessageContentSection(),
9155
9832
  cliCommandsSection(),
9156
9833
  messagingSection(),
9157
9834
  channelTypesSection(),
@@ -10131,6 +10808,10 @@ ${this.opts.wakePromptFooter}` : text;
10131
10808
  sent = session.send(input);
10132
10809
  if (exactOwner)
10133
10810
  this.markPendingDeliveryObserved(exactOwner, input.id);
10811
+ this.log.info("steering message submitted to logical session", {
10812
+ agentId: effect.agentId,
10813
+ mode: effect.mode
10814
+ });
10134
10815
  } catch (error) {
10135
10816
  if (exactOwner) {
10136
10817
  this.settlePendingDelivery(exactOwner, input.id);
@@ -10176,7 +10857,6 @@ ${this.opts.wakePromptFooter}` : text;
10176
10857
  mode: effect.mode
10177
10858
  });
10178
10859
  }
10179
- this.log.info("steering message sent to running agent", { agentId: effect.agentId, mode: effect.mode });
10180
10860
  break;
10181
10861
  }
10182
10862
  case "stop":
@@ -11699,10 +12379,10 @@ function mergeDefs(...defs) {
11699
12379
  function cloneDef(schema) {
11700
12380
  return mergeDefs(schema._zod.def);
11701
12381
  }
11702
- function getElementAtPath(obj, path8) {
11703
- if (!path8)
12382
+ function getElementAtPath(obj, path12) {
12383
+ if (!path12)
11704
12384
  return obj;
11705
- return path8.reduce((acc, key) => acc?.[key], obj);
12385
+ return path12.reduce((acc, key) => acc?.[key], obj);
11706
12386
  }
11707
12387
  function promiseAllObject(promisesObj) {
11708
12388
  const keys = Object.keys(promisesObj);
@@ -12110,11 +12790,11 @@ function explicitlyAborted(x, startIndex = 0) {
12110
12790
  }
12111
12791
  return false;
12112
12792
  }
12113
- function prefixIssues(path8, issues) {
12793
+ function prefixIssues(path12, issues) {
12114
12794
  return issues.map((iss) => {
12115
12795
  var _a2;
12116
12796
  (_a2 = iss).path ?? (_a2.path = []);
12117
- iss.path.unshift(path8);
12797
+ iss.path.unshift(path12);
12118
12798
  return iss;
12119
12799
  });
12120
12800
  }
@@ -12261,16 +12941,16 @@ function flattenError(error, mapper = (issue2) => issue2.message) {
12261
12941
  }
12262
12942
  function formatError(error, mapper = (issue2) => issue2.message) {
12263
12943
  const fieldErrors = { _errors: [] };
12264
- const processError = (error2, path8 = []) => {
12944
+ const processError = (error2, path12 = []) => {
12265
12945
  for (const issue2 of error2.issues) {
12266
12946
  if (issue2.code === "invalid_union" && issue2.errors.length) {
12267
- issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
12947
+ issue2.errors.map((issues) => processError({ issues }, [...path12, ...issue2.path]));
12268
12948
  } else if (issue2.code === "invalid_key") {
12269
- processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
12949
+ processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
12270
12950
  } else if (issue2.code === "invalid_element") {
12271
- processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
12951
+ processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
12272
12952
  } else {
12273
- const fullpath = [...path8, ...issue2.path];
12953
+ const fullpath = [...path12, ...issue2.path];
12274
12954
  if (fullpath.length === 0) {
12275
12955
  fieldErrors._errors.push(mapper(issue2));
12276
12956
  } else {
@@ -12297,17 +12977,17 @@ function formatError(error, mapper = (issue2) => issue2.message) {
12297
12977
  }
12298
12978
  function treeifyError(error, mapper = (issue2) => issue2.message) {
12299
12979
  const result = { errors: [] };
12300
- const processError = (error2, path8 = []) => {
12980
+ const processError = (error2, path12 = []) => {
12301
12981
  var _a2, _b;
12302
12982
  for (const issue2 of error2.issues) {
12303
12983
  if (issue2.code === "invalid_union" && issue2.errors.length) {
12304
- issue2.errors.map((issues) => processError({ issues }, [...path8, ...issue2.path]));
12984
+ issue2.errors.map((issues) => processError({ issues }, [...path12, ...issue2.path]));
12305
12985
  } else if (issue2.code === "invalid_key") {
12306
- processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
12986
+ processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
12307
12987
  } else if (issue2.code === "invalid_element") {
12308
- processError({ issues: issue2.issues }, [...path8, ...issue2.path]);
12988
+ processError({ issues: issue2.issues }, [...path12, ...issue2.path]);
12309
12989
  } else {
12310
- const fullpath = [...path8, ...issue2.path];
12990
+ const fullpath = [...path12, ...issue2.path];
12311
12991
  if (fullpath.length === 0) {
12312
12992
  result.errors.push(mapper(issue2));
12313
12993
  continue;
@@ -12339,8 +13019,8 @@ function treeifyError(error, mapper = (issue2) => issue2.message) {
12339
13019
  }
12340
13020
  function toDotPath(_path) {
12341
13021
  const segs = [];
12342
- const path8 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12343
- for (const seg of path8) {
13022
+ const path12 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
13023
+ for (const seg of path12) {
12344
13024
  if (typeof seg === "number")
12345
13025
  segs.push(`[${seg}]`);
12346
13026
  else if (typeof seg === "symbol")
@@ -24799,13 +25479,13 @@ function resolveRef(ref, ctx) {
24799
25479
  if (!ref.startsWith("#")) {
24800
25480
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
24801
25481
  }
24802
- const path8 = ref.slice(1).split("/").filter(Boolean);
24803
- if (path8.length === 0) {
25482
+ const path12 = ref.slice(1).split("/").filter(Boolean);
25483
+ if (path12.length === 0) {
24804
25484
  return ctx.rootSchema;
24805
25485
  }
24806
25486
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
24807
- if (path8[0] === defsKey) {
24808
- const key = path8[1];
25487
+ if (path12[0] === defsKey) {
25488
+ const key = path12[1];
24809
25489
  if (!key || !ctx.defs[key]) {
24810
25490
  throw new Error(`Reference not found: ${ref}`);
24811
25491
  }
@@ -26722,6 +27402,7 @@ __export(exports_schema, {
26722
27402
  user: () => user,
26723
27403
  taskMessage: () => taskMessage,
26724
27404
  session: () => session,
27405
+ nativeOauthAttempt: () => nativeOauthAttempt,
26725
27406
  messageFlag: () => messageFlag,
26726
27407
  message: () => message,
26727
27408
  member: () => member,
@@ -26748,7 +27429,11 @@ __export(exports_schema, {
26748
27429
  agentEmailAccount: () => agentEmailAccount,
26749
27430
  agentAccess: () => agentAccess,
26750
27431
  agent: () => agent,
26751
- account: () => account
27432
+ account: () => account,
27433
+ NATIVE_OAUTH_STATUSES: () => NATIVE_OAUTH_STATUSES,
27434
+ NATIVE_OAUTH_PROVIDERS: () => NATIVE_OAUTH_PROVIDERS,
27435
+ NATIVE_OAUTH_PLATFORMS: () => NATIVE_OAUTH_PLATFORMS,
27436
+ NATIVE_OAUTH_FAILURE_CODES: () => NATIVE_OAUTH_FAILURE_CODES
26752
27437
  });
26753
27438
  init_nanoid();
26754
27439
 
@@ -26813,6 +27498,7 @@ var TERMINAL_MEETING_STATUSES = [
26813
27498
  MeetingStatus.COMPLETED,
26814
27499
  MeetingStatus.FAILED
26815
27500
  ];
27501
+ var COMMUNITY_BOT_LIMIT_PER_OWNER = 20;
26816
27502
  var COMMUNITY_BOT_NAME_MIN = 1;
26817
27503
  var COMMUNITY_BOT_NAME_MAX = 32;
26818
27504
  var COMMUNITY_BOT_DESCRIPTION_MAX = 1024;
@@ -26878,6 +27564,119 @@ var verification = sqliteTable("verification", {
26878
27564
  createdAt: text("createdAt").notNull().$defaultFn(() => new Date().toISOString()),
26879
27565
  updatedAt: text("updatedAt").notNull().$defaultFn(() => new Date().toISOString())
26880
27566
  });
27567
+ var NATIVE_OAUTH_PROVIDERS = ["github", "google"];
27568
+ var NATIVE_OAUTH_PLATFORMS = [
27569
+ "macos",
27570
+ "windows",
27571
+ "linux",
27572
+ "ios",
27573
+ "android"
27574
+ ];
27575
+ var NATIVE_OAUTH_STATUSES = [
27576
+ "pending",
27577
+ "opened",
27578
+ "ready",
27579
+ "exchanging",
27580
+ "consumed",
27581
+ "failed",
27582
+ "cancelled",
27583
+ "replaced"
27584
+ ];
27585
+ var NATIVE_OAUTH_FAILURE_CODES = [
27586
+ "access_denied",
27587
+ "provider_error",
27588
+ "oauth_callback_failed",
27589
+ "start_failed",
27590
+ "invalid_handoff"
27591
+ ];
27592
+ var nativeOauthAttempt = sqliteTable("native_oauth_attempt", {
27593
+ id: text("id").primaryKey(),
27594
+ instanceKeyHash: text("instance_key_hash").notNull(),
27595
+ stateHash: text("state_hash").notNull(),
27596
+ pkceChallenge: text("pkce_challenge").notNull(),
27597
+ provider: text("provider").$type().notNull(),
27598
+ platform: text("platform").$type().notNull(),
27599
+ redirectPath: text("redirect_path").notNull(),
27600
+ status: text("status").$type().notNull().default("pending"),
27601
+ handoffCodeHash: text("handoff_code_hash"),
27602
+ handoffExpiresAt: integer2("handoff_expires_at"),
27603
+ authKind: text("auth_kind").$type(),
27604
+ failureCode: text("failure_code").$type(),
27605
+ attemptExpiresAt: integer2("attempt_expires_at").notNull(),
27606
+ createdAt: integer2("created_at").notNull(),
27607
+ updatedAt: integer2("updated_at").notNull(),
27608
+ openedAt: integer2("opened_at"),
27609
+ readyAt: integer2("ready_at"),
27610
+ consumedAt: integer2("consumed_at"),
27611
+ failedAt: integer2("failed_at"),
27612
+ cancelledAt: integer2("cancelled_at"),
27613
+ replacedAt: integer2("replaced_at")
27614
+ }, (t) => [
27615
+ index("idx_native_oauth_attempt_instance_status").on(t.instanceKeyHash, t.status),
27616
+ index("idx_native_oauth_attempt_id_status_expiry").on(t.id, t.status, t.attemptExpiresAt),
27617
+ index("idx_native_oauth_attempt_terminal_cleanup").on(t.status, t.updatedAt),
27618
+ uniqueIndex("uq_native_oauth_attempt_instance_live").on(t.instanceKeyHash).where(sql`status IN ('pending', 'opened', 'ready', 'exchanging')`),
27619
+ uniqueIndex("uq_native_oauth_attempt_handoff_hash").on(t.handoffCodeHash).where(sql`handoff_code_hash IS NOT NULL`),
27620
+ check2("ck_native_oauth_attempt_id", sql`length(${t.id}) BETWEEN 22 AND 64 AND ${t.id} NOT GLOB '*[^A-Za-z0-9_-]*'`),
27621
+ check2("ck_native_oauth_attempt_hashes", sql`length(${t.instanceKeyHash}) = 64 AND ${t.instanceKeyHash} NOT GLOB '*[^0-9a-f]*'
27622
+ AND length(${t.stateHash}) = 64 AND ${t.stateHash} NOT GLOB '*[^0-9a-f]*'
27623
+ AND length(${t.pkceChallenge}) = 43 AND ${t.pkceChallenge} NOT GLOB '*[^A-Za-z0-9_-]*'
27624
+ AND (${t.handoffCodeHash} IS NULL OR (length(${t.handoffCodeHash}) = 64 AND ${t.handoffCodeHash} NOT GLOB '*[^0-9a-f]*'))`),
27625
+ check2("ck_native_oauth_attempt_enums", sql`${t.provider} IN ('github', 'google')
27626
+ AND ${t.platform} IN ('macos', 'windows', 'linux', 'ios', 'android')
27627
+ AND ${t.status} IN ('pending', 'opened', 'ready', 'exchanging', 'consumed', 'failed', 'cancelled', 'replaced')
27628
+ AND (${t.authKind} IS NULL OR ${t.authKind} IN ('signin', 'signup'))
27629
+ AND (${t.failureCode} IS NULL OR ${t.failureCode} IN ('access_denied', 'provider_error', 'oauth_callback_failed', 'start_failed', 'invalid_handoff'))`),
27630
+ check2("ck_native_oauth_attempt_redirect", sql`length(${t.redirectPath}) BETWEEN 1 AND 2048
27631
+ AND substr(${t.redirectPath}, 1, 1) = '/'
27632
+ AND substr(${t.redirectPath}, 1, 2) <> '//'
27633
+ AND instr(${t.redirectPath}, char(92)) = 0`),
27634
+ check2("ck_native_oauth_attempt_epochs", sql`typeof(${t.createdAt}) = 'integer' AND ${t.createdAt} BETWEEN 0 AND 9007199254740991
27635
+ AND typeof(${t.updatedAt}) = 'integer' AND ${t.updatedAt} BETWEEN ${t.createdAt} AND 9007199254740991
27636
+ AND typeof(${t.attemptExpiresAt}) = 'integer' AND ${t.attemptExpiresAt} = ${t.createdAt} + 600000
27637
+ AND (${t.handoffExpiresAt} IS NULL OR (typeof(${t.handoffExpiresAt}) = 'integer' AND ${t.handoffExpiresAt} <= ${t.attemptExpiresAt}))`),
27638
+ check2("ck_native_oauth_attempt_state", sql`(
27639
+ ${t.status} = 'pending'
27640
+ AND ${t.openedAt} IS NULL AND ${t.readyAt} IS NULL AND ${t.consumedAt} IS NULL
27641
+ AND ${t.failedAt} IS NULL AND ${t.cancelledAt} IS NULL AND ${t.replacedAt} IS NULL
27642
+ AND ${t.handoffCodeHash} IS NULL AND ${t.handoffExpiresAt} IS NULL
27643
+ AND ${t.authKind} IS NULL AND ${t.failureCode} IS NULL
27644
+ ) OR (
27645
+ ${t.status} = 'opened'
27646
+ AND ${t.openedAt} IS NOT NULL AND ${t.readyAt} IS NULL AND ${t.consumedAt} IS NULL
27647
+ AND ${t.failedAt} IS NULL AND ${t.cancelledAt} IS NULL AND ${t.replacedAt} IS NULL
27648
+ AND ${t.handoffCodeHash} IS NULL AND ${t.handoffExpiresAt} IS NULL
27649
+ AND ${t.authKind} IS NULL AND ${t.failureCode} IS NULL
27650
+ ) OR (
27651
+ ${t.status} IN ('ready', 'exchanging')
27652
+ AND ${t.openedAt} IS NOT NULL AND ${t.readyAt} IS NOT NULL AND ${t.consumedAt} IS NULL
27653
+ AND ${t.failedAt} IS NULL AND ${t.cancelledAt} IS NULL AND ${t.replacedAt} IS NULL
27654
+ AND ${t.handoffCodeHash} IS NOT NULL AND ${t.handoffExpiresAt} IS NOT NULL
27655
+ AND ${t.handoffExpiresAt} = ${t.readyAt} + 120000
27656
+ AND ${t.authKind} IS NOT NULL AND ${t.failureCode} IS NULL
27657
+ ) OR (
27658
+ ${t.status} = 'consumed'
27659
+ AND ${t.openedAt} IS NOT NULL AND ${t.readyAt} IS NOT NULL AND ${t.consumedAt} IS NOT NULL
27660
+ AND ${t.failedAt} IS NULL AND ${t.cancelledAt} IS NULL AND ${t.replacedAt} IS NULL
27661
+ AND ${t.handoffCodeHash} IS NOT NULL AND ${t.handoffExpiresAt} IS NOT NULL
27662
+ AND ${t.authKind} IS NOT NULL AND ${t.failureCode} IS NULL
27663
+ ) OR (
27664
+ ${t.status} = 'failed'
27665
+ AND ${t.consumedAt} IS NULL AND ${t.failedAt} IS NOT NULL
27666
+ AND ${t.cancelledAt} IS NULL AND ${t.replacedAt} IS NULL
27667
+ AND ${t.failureCode} IS NOT NULL
27668
+ ) OR (
27669
+ ${t.status} = 'cancelled'
27670
+ AND ${t.consumedAt} IS NULL AND ${t.cancelledAt} IS NOT NULL
27671
+ AND ${t.failedAt} IS NULL AND ${t.replacedAt} IS NULL
27672
+ AND ${t.failureCode} IS NULL
27673
+ ) OR (
27674
+ ${t.status} = 'replaced'
27675
+ AND ${t.consumedAt} IS NULL AND ${t.replacedAt} IS NOT NULL
27676
+ AND ${t.failedAt} IS NULL AND ${t.cancelledAt} IS NULL
27677
+ AND ${t.failureCode} IS NULL
27678
+ )`)
27679
+ ]);
26881
27680
  var workspace = sqliteTable("workspace", {
26882
27681
  id: text("id").primaryKey().$defaultFn(() => "sp_" + nanoid3()),
26883
27682
  name: text("name").notNull(),
@@ -27687,6 +28486,13 @@ var HostCommandSchema = exports_external.discriminatedUnion("type", [
27687
28486
  launchId: exports_external.string().min(1),
27688
28487
  unreadNotice: exports_external.unknown()
27689
28488
  }),
28489
+ exports_external.object({
28490
+ type: exports_external.literal("agent:event"),
28491
+ agentId: exports_external.string().min(1),
28492
+ config: exports_external.unknown(),
28493
+ launchId: exports_external.string().min(1),
28494
+ prompt: exports_external.string().min(1).max(32768)
28495
+ }),
27690
28496
  exports_external.object({
27691
28497
  type: exports_external.literal("agent:stop"),
27692
28498
  agentId: exports_external.string().min(1)
@@ -28038,6 +28844,26 @@ class AgentRouter {
28038
28844
  return;
28039
28845
  }
28040
28846
  break;
28847
+ case "agent:event":
28848
+ this.log.info("agent:event received", { agentId: cmd.agentId });
28849
+ try {
28850
+ await this.opts.onBeforeAgent?.(cmd.agentId);
28851
+ this.opts.manager.register(cmd.agentId, {
28852
+ runtimeConfig: cmd.config,
28853
+ launchId: cmd.launchId
28854
+ });
28855
+ this.running.add(cmd.agentId);
28856
+ this.opts.manager.deliver(cmd.agentId, {
28857
+ id: `${cmd.agentId}:event:${cmd.launchId}`,
28858
+ text: cmd.prompt
28859
+ });
28860
+ } catch (err) {
28861
+ this.log.warn("agent:event failed", {
28862
+ agentId: cmd.agentId,
28863
+ err: err instanceof Error ? err.message : String(err)
28864
+ });
28865
+ }
28866
+ break;
28041
28867
  case "agent:reset":
28042
28868
  await this.runRestartCommand(cmd.agentId, cmd.launchId, "agent:reset", () => this.opts.manager.resetSession(cmd.agentId, {
28043
28869
  runtimeConfig: cmd.config,
@@ -28155,7 +28981,7 @@ import * as fs5 from "fs";
28155
28981
  import * as http from "http";
28156
28982
  import * as https from "https";
28157
28983
  import * as os2 from "os";
28158
- import * as path8 from "path";
28984
+ import * as path12 from "path";
28159
28985
  import { URL as URL2 } from "url";
28160
28986
  var LOCAL_MESSAGE_REMINDER_PATH = "/__alook/local/message-reminder";
28161
28987
  var LOCAL_MESSAGE_REMINDER_BODY_MAX_BYTES = 4 * 1024;
@@ -28187,15 +29013,15 @@ class CredentialBroker {
28187
29013
  this.voucherPrefix = config2.voucherPrefix ?? "vch_";
28188
29014
  this.clientLabel = config2.clientLabel ?? "cli";
28189
29015
  this.headerNames = config2.headerNames ?? DEFAULT_HEADER_NAMES;
28190
- this.voucherDir = config2.voucherDir ?? path8.join(os2.tmpdir(), "agent-vouchers");
29016
+ this.voucherDir = config2.voucherDir ?? path12.join(os2.tmpdir(), "agent-vouchers");
28191
29017
  }
28192
29018
  mint(agentId, launchId, capabilities2, runnerKey) {
28193
29019
  if (!runnerKey)
28194
29020
  throw new Error("CredentialBroker.mint: runnerKey is required (per-agent tier-2 credential)");
28195
29021
  const voucher = randomVoucher(this.voucherPrefix);
28196
- const dir = path8.join(this.voucherDir, sanitizeIdSegment(agentId));
29022
+ const dir = path12.join(this.voucherDir, sanitizeIdSegment(agentId));
28197
29023
  fs5.mkdirSync(dir, { recursive: true });
28198
- const voucherFile = path8.join(dir, `${sanitizeIdSegment(launchId)}.token`);
29024
+ const voucherFile = path12.join(dir, `${sanitizeIdSegment(launchId)}.token`);
28199
29025
  fs5.writeFileSync(voucherFile, voucher, { mode: 384 });
28200
29026
  this.registrations.set(voucher, {
28201
29027
  agentId,
@@ -28392,12 +29218,20 @@ async function startCredentialProxy(broker, options = {}) {
28392
29218
  const upstream = new URL2(broker.upstreamBaseUrl);
28393
29219
  const upstreamClient = upstream.protocol === "https:" ? https : http;
28394
29220
  const onPull = options.onInboxPullResponse;
29221
+ const onAck = options.onInboxAckResponse;
28395
29222
  const onProxyRequest = options.onProxyRequest;
28396
29223
  const reportInboxPullObservationError = (failure) => {
28397
29224
  try {
28398
29225
  options.onInboxPullObservationError?.(failure);
28399
29226
  } catch {}
28400
29227
  };
29228
+ const reportInboxAckObservationError = (failure) => {
29229
+ try {
29230
+ options.onInboxAckObservationError?.(failure);
29231
+ } catch {
29232
+ return;
29233
+ }
29234
+ };
28401
29235
  const server = http.createServer((req, res) => {
28402
29236
  const pathname = new URL2(req.url ?? "/", "http://placeholder").pathname;
28403
29237
  if (pathname.startsWith("/__alook/local/")) {
@@ -28437,8 +29271,11 @@ async function startCredentialProxy(broker, options = {}) {
28437
29271
  }
28438
29272
  const reg = verdict.reg;
28439
29273
  const isInboxPull = req.method === "POST" && pathname === "/api/community/users/me/inbox/pull";
29274
+ const isInboxAck = req.method === "POST" && pathname === "/api/community/users/me/inbox/ack";
28440
29275
  const shouldObserveInboxPull = Boolean(isInboxPull && onPull);
29276
+ const shouldObserveInboxAck = Boolean(isInboxAck && onAck);
28441
29277
  let inboxPullObservationToken;
29278
+ let inboxAckObservationToken;
28442
29279
  if (shouldObserveInboxPull) {
28443
29280
  try {
28444
29281
  inboxPullObservationToken = options.onInboxPullStart?.(reg.agentId);
@@ -28450,6 +29287,17 @@ async function startCredentialProxy(broker, options = {}) {
28450
29287
  });
28451
29288
  }
28452
29289
  }
29290
+ if (shouldObserveInboxAck) {
29291
+ try {
29292
+ inboxAckObservationToken = options.onInboxAckStart?.(reg.agentId);
29293
+ } catch {
29294
+ reportInboxAckObservationError({
29295
+ agentId: reg.agentId,
29296
+ reason: "observer_failed",
29297
+ contentEncoding: "identity"
29298
+ });
29299
+ }
29300
+ }
28453
29301
  if (onProxyRequest) {
28454
29302
  try {
28455
29303
  const method = req.method ?? "GET";
@@ -28467,7 +29315,7 @@ async function startCredentialProxy(broker, options = {}) {
28467
29315
  outHeaders[broker.headerNames.agentId.toLowerCase()] = reg.agentId;
28468
29316
  outHeaders[broker.headerNames.client.toLowerCase()] = broker.clientLabel;
28469
29317
  outHeaders[broker.headerNames.capabilities.toLowerCase()] = [...reg.capabilities].join(",");
28470
- if (isInboxPull)
29318
+ if (isInboxPull || isInboxAck)
28471
29319
  outHeaders["accept-encoding"] = "identity";
28472
29320
  let responded = false;
28473
29321
  let upstreamRes;
@@ -28487,43 +29335,72 @@ async function startCredentialProxy(broker, options = {}) {
28487
29335
  };
28488
29336
  res_.on("error", destroyResIfIncomplete);
28489
29337
  res_.on("close", destroyResIfIncomplete);
28490
- if (shouldObserveInboxPull && res_.statusCode !== undefined && res_.statusCode >= 200 && res_.statusCode < 300) {
29338
+ if ((shouldObserveInboxPull || shouldObserveInboxAck) && res_.statusCode !== undefined && res_.statusCode >= 200 && res_.statusCode < 300) {
28491
29339
  const chunks = [];
28492
29340
  res_.on("data", (chunk) => chunks.push(chunk));
28493
29341
  res_.on("end", () => {
28494
29342
  const body = Buffer.concat(chunks);
28495
29343
  const contentEncoding = normalizeContentEncoding(res_.headers["content-encoding"]);
28496
29344
  if (contentEncoding !== "identity") {
28497
- reportInboxPullObservationError({
28498
- agentId: reg.agentId,
28499
- reason: "unexpected_content_encoding",
28500
- contentEncoding
28501
- });
29345
+ if (shouldObserveInboxPull) {
29346
+ reportInboxPullObservationError({
29347
+ agentId: reg.agentId,
29348
+ reason: "unexpected_content_encoding",
29349
+ contentEncoding
29350
+ });
29351
+ } else {
29352
+ reportInboxAckObservationError({
29353
+ agentId: reg.agentId,
29354
+ reason: "unexpected_content_encoding",
29355
+ contentEncoding
29356
+ });
29357
+ }
28502
29358
  } else {
28503
29359
  let parsed;
28504
29360
  try {
28505
29361
  parsed = JSON.parse(body.toString("utf8"));
28506
29362
  } catch {
28507
- reportInboxPullObservationError({
28508
- agentId: reg.agentId,
28509
- reason: "invalid_json",
28510
- contentEncoding
28511
- });
29363
+ if (shouldObserveInboxPull) {
29364
+ reportInboxPullObservationError({ agentId: reg.agentId, reason: "invalid_json", contentEncoding });
29365
+ } else {
29366
+ reportInboxAckObservationError({ agentId: reg.agentId, reason: "invalid_json", contentEncoding });
29367
+ }
28512
29368
  }
28513
29369
  if (parsed !== undefined) {
28514
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || !Array.isArray(parsed.messages)) {
28515
- reportInboxPullObservationError({
29370
+ if (shouldObserveInboxPull) {
29371
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || !Array.isArray(parsed.messages)) {
29372
+ reportInboxPullObservationError({
29373
+ agentId: reg.agentId,
29374
+ reason: "invalid_inbox_shape",
29375
+ contentEncoding
29376
+ });
29377
+ } else {
29378
+ const messages = parsed.messages;
29379
+ if (messages.length > 0) {
29380
+ try {
29381
+ onPull(reg.agentId, messages, inboxPullObservationToken);
29382
+ } catch {
29383
+ reportInboxPullObservationError({
29384
+ agentId: reg.agentId,
29385
+ reason: "observer_failed",
29386
+ contentEncoding
29387
+ });
29388
+ }
29389
+ }
29390
+ }
29391
+ } else if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || !Array.isArray(parsed.applied) || !parsed.applied.every((cursor) => Boolean(cursor) && typeof cursor === "object" && !Array.isArray(cursor) && typeof cursor.channel === "string" && cursor.channel.length > 0 && typeof cursor.seq === "number" && Number.isSafeInteger(cursor.seq) && cursor.seq > 0)) {
29392
+ reportInboxAckObservationError({
28516
29393
  agentId: reg.agentId,
28517
- reason: "invalid_inbox_shape",
29394
+ reason: "invalid_ack_shape",
28518
29395
  contentEncoding
28519
29396
  });
28520
29397
  } else {
28521
- const messages = parsed.messages;
28522
- if (messages.length > 0) {
29398
+ const applied = parsed.applied;
29399
+ if (applied.length > 0) {
28523
29400
  try {
28524
- onPull(reg.agentId, messages, inboxPullObservationToken);
29401
+ onAck(reg.agentId, applied, inboxAckObservationToken);
28525
29402
  } catch {
28526
- reportInboxPullObservationError({
29403
+ reportInboxAckObservationError({
28527
29404
  agentId: reg.agentId,
28528
29405
  reason: "observer_failed",
28529
29406
  contentEncoding
@@ -28590,7 +29467,7 @@ function joinPath(basePath, reqUrl) {
28590
29467
  return base + reqPath || "/";
28591
29468
  }
28592
29469
  // src/daemon/createDaemon.ts
28593
- import { homedir as homedir5 } from "os";
29470
+ import { homedir as homedir7 } from "os";
28594
29471
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync7 } from "node:fs";
28595
29472
 
28596
29473
  // src/util/rotatingFileSink.ts
@@ -28607,7 +29484,7 @@ import {
28607
29484
  statSync,
28608
29485
  unlinkSync as unlinkSync3
28609
29486
  } from "node:fs";
28610
- function createRotatingFileSink(path9, maxBytes, opts = {}) {
29487
+ function createRotatingFileSink(path13, maxBytes, opts = {}) {
28611
29488
  const report = (operation, error51) => {
28612
29489
  try {
28613
29490
  opts.onError?.({ operation, error: error51 });
@@ -28617,8 +29494,8 @@ function createRotatingFileSink(path9, maxBytes, opts = {}) {
28617
29494
  if (!existsSync5(filePath))
28618
29495
  return true;
28619
29496
  try {
28620
- const stat = lstatSync2(filePath);
28621
- if (!stat.isFile()) {
29497
+ const stat2 = lstatSync2(filePath);
29498
+ if (!stat2.isFile()) {
28622
29499
  if (opts.mode === undefined && !opts.hardMaxBytes)
28623
29500
  return true;
28624
29501
  report("unsafe_generation", new Error("log generation is not a regular file"));
@@ -28626,7 +29503,7 @@ function createRotatingFileSink(path9, maxBytes, opts = {}) {
28626
29503
  }
28627
29504
  if (opts.mode !== undefined)
28628
29505
  chmodSync(filePath, opts.mode);
28629
- if (opts.hardMaxBytes && maxBytes > 0 && stat.size > maxBytes) {
29506
+ if (opts.hardMaxBytes && maxBytes > 0 && stat2.size > maxBytes) {
28630
29507
  try {
28631
29508
  unlinkSync3(filePath);
28632
29509
  } catch (error51) {
@@ -28643,9 +29520,9 @@ function createRotatingFileSink(path9, maxBytes, opts = {}) {
28643
29520
  };
28644
29521
  const rotate = () => {
28645
29522
  try {
28646
- renameSync(path9, `${path9}.1`);
29523
+ renameSync(path13, `${path13}.1`);
28647
29524
  if (opts.mode !== undefined)
28648
- chmodSync(`${path9}.1`, opts.mode);
29525
+ chmodSync(`${path13}.1`, opts.mode);
28649
29526
  return true;
28650
29527
  } catch (error51) {
28651
29528
  report("rotate", error51);
@@ -28654,7 +29531,7 @@ function createRotatingFileSink(path9, maxBytes, opts = {}) {
28654
29531
  };
28655
29532
  const currentSize = () => {
28656
29533
  try {
28657
- return existsSync5(path9) ? statSync(path9).size : 0;
29534
+ return existsSync5(path13) ? statSync(path13).size : 0;
28658
29535
  } catch (error51) {
28659
29536
  report("stat", error51);
28660
29537
  return null;
@@ -28662,7 +29539,7 @@ function createRotatingFileSink(path9, maxBytes, opts = {}) {
28662
29539
  };
28663
29540
  const sink = {
28664
29541
  secure() {
28665
- return secureGeneration(`${path9}.1`) && secureGeneration(path9);
29542
+ return secureGeneration(`${path13}.1`) && secureGeneration(path13);
28666
29543
  },
28667
29544
  write(line) {
28668
29545
  try {
@@ -28682,7 +29559,7 @@ function createRotatingFileSink(path9, maxBytes, opts = {}) {
28682
29559
  const shouldRotate = maxBytes > 0 && (opts.hardMaxBytes ? currentBytes > 0 && currentBytes + serializedBytes > maxBytes : currentBytes >= maxBytes);
28683
29560
  if (shouldRotate && !rotate() && opts.hardMaxBytes)
28684
29561
  return;
28685
- appendFileSync(path9, serialized, opts.mode === undefined ? undefined : { mode: opts.mode });
29562
+ appendFileSync(path13, serialized, opts.mode === undefined ? undefined : { mode: opts.mode });
28686
29563
  } catch (error51) {
28687
29564
  report("append", error51);
28688
29565
  }
@@ -28690,7 +29567,7 @@ function createRotatingFileSink(path9, maxBytes, opts = {}) {
28690
29567
  openSnapshot() {
28691
29568
  const files = [];
28692
29569
  try {
28693
- for (const candidate of [`${path9}.1`, path9]) {
29570
+ for (const candidate of [`${path13}.1`, path13]) {
28694
29571
  if (!existsSync5(candidate))
28695
29572
  continue;
28696
29573
  if (!secureGeneration(candidate))
@@ -28698,10 +29575,10 @@ function createRotatingFileSink(path9, maxBytes, opts = {}) {
28698
29575
  const noFollow = "O_NOFOLLOW" in constants ? constants.O_NOFOLLOW : 0;
28699
29576
  const fd = openSync(candidate, constants.O_RDONLY | noFollow);
28700
29577
  try {
28701
- const stat = fstatSync(fd);
28702
- if (!stat.isFile())
29578
+ const stat2 = fstatSync(fd);
29579
+ if (!stat2.isFile())
28703
29580
  throw new Error("snapshot source is not a regular file");
28704
- files.push({ path: candidate, fd, size: stat.size });
29581
+ files.push({ path: candidate, fd, size: stat2.size });
28705
29582
  } catch (error51) {
28706
29583
  closeSync(fd);
28707
29584
  throw error51;
@@ -28851,11 +29728,11 @@ function createTraceSampler(emit, throttleMs = DEFAULT_TRACE_SAMPLE_MS) {
28851
29728
 
28852
29729
  // src/util/statusFile.ts
28853
29730
  import { writeFileSync as writeFileSync4, renameSync as renameSync2 } from "node:fs";
28854
- function writeStatusFile(path9, snapshot) {
29731
+ function writeStatusFile(path13, snapshot) {
28855
29732
  try {
28856
- const tmp = `${path9}.tmp`;
29733
+ const tmp = `${path13}.tmp`;
28857
29734
  writeFileSync4(tmp, JSON.stringify(snapshot));
28858
- renameSync2(tmp, path9);
29735
+ renameSync2(tmp, path13);
28859
29736
  } catch {}
28860
29737
  }
28861
29738
  // ../shared/src/constants/community.ts
@@ -29699,6 +30576,10 @@ var CommunityBotPatchRequestSchema = exports_external.object({
29699
30576
  var CommunityBotAddToServerRequestSchema = exports_external.object({
29700
30577
  botId: exports_external.string().min(1)
29701
30578
  });
30579
+ var CommunityServerOnboardRequestSchema = exports_external.strictObject({
30580
+ botIds: exports_external.array(exports_external.string().min(1).max(128)).min(1).max(COMMUNITY_BOT_LIMIT_PER_OWNER).refine((ids) => new Set(ids).size === ids.length, "botIds must be unique"),
30581
+ wakePrompt: exports_external.string().max(32768).refine((value) => value.trim().length > 0, "wakePrompt is required")
30582
+ });
29702
30583
  var CommunityAgentMessageContentSchema = exports_external.object({ text: exports_external.string().max(MAX_MESSAGE_CONTENT_LENGTH).default("") }).catchall(exports_external.unknown());
29703
30584
  var CommunityAgentSeqSchema = exports_external.number().int().min(0);
29704
30585
  var CommunityAgentPositiveSeqSchema = exports_external.number().int().min(1);
@@ -30302,6 +31183,15 @@ function createLogger2(opts) {
30302
31183
 
30303
31184
  // ../shared/src/db/queries/_chunk.ts
30304
31185
  var D1_MAX_BIND_PARAMS = 100;
31186
+ function maxInParams(fixedParams) {
31187
+ if (!Number.isInteger(fixedParams) || fixedParams < 0) {
31188
+ throw new Error("fixed params must be a non-negative integer");
31189
+ }
31190
+ if (fixedParams >= D1_MAX_BIND_PARAMS) {
31191
+ throw new Error("fixed params must leave room for at least one IN parameter");
31192
+ }
31193
+ return D1_MAX_BIND_PARAMS - fixedParams;
31194
+ }
30305
31195
  function maxRowsPerInsert(paramsPerRow) {
30306
31196
  if (paramsPerRow < 1)
30307
31197
  throw new Error("paramsPerRow must be >= 1");
@@ -30393,6 +31283,7 @@ var AGENT_MESSAGE_COLUMNS = {
30393
31283
  replyToId: communityMessage.replyToId
30394
31284
  };
30395
31285
  var channelJoinBaselineGuard = sql`${communityMessage.createdAt} > COALESCE(${communityChannelMember.addedAt}, ${communityServerMember.joinedAt}, '')`;
31286
+ var AGENT_UNREAD_CHANNEL_CHUNK_SIZE = maxInParams(13);
30396
31287
 
30397
31288
  // ../shared/src/db/queries/community/mention.ts
30398
31289
  var MENTION_INSERT_MAX_ROWS = maxRowsPerInsert(5);
@@ -31005,6 +31896,10 @@ var serverRailCommandSchema = exports_external.discriminatedUnion("kind", [
31005
31896
  var serverRailCommitRequestSchema = exports_external.strictObject({
31006
31897
  commands: exports_external.array(serverRailCommandSchema).min(1).max(MAX_SERVER_RAIL_COMMANDS)
31007
31898
  });
31899
+ // ../shared/src/db/queries/native-oauth.ts
31900
+ var ATTEMPT_TTL_MS = 10 * 60 * 1000;
31901
+ var HANDOFF_TTL_MS = 2 * 60 * 1000;
31902
+ var RETENTION_MS = 24 * 60 * 60 * 1000;
31008
31903
  // ../shared/src/db/queries/task.ts
31009
31904
  var DEFAULT_STALE_SECONDS = Number(process.env.ALOOK_STALE_DISPATCH_TIMEOUT_S) || 20;
31010
31905
  var DEFAULT_STALE_RUNNING_SECONDS = Number(process.env.ALOOK_STALE_RUNNING_TIMEOUT_S) || 3600;
@@ -31033,6 +31928,80 @@ var FTS_KEYWORDS = new Set(["and", "or", "not", "near"]);
31033
31928
  var FOLDER_ITEM_INSERT_MAX_ROWS = maxRowsPerInsert(3);
31034
31929
  // ../shared/src/db/queries/community/diagnostic-report.ts
31035
31930
  var diagnosticOwner = alias(user, "diagnostic_owner");
31931
+ // ../shared/src/lib/safe-redirect.ts
31932
+ var MAX_REDIRECT_BYTES = 2048;
31933
+ var CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/;
31934
+ function isSafeCandidate(value) {
31935
+ return value.startsWith("/") && !value.startsWith("//") && !value.includes("\\") && !value.includes("#") && !CONTROL_CHARACTERS.test(value);
31936
+ }
31937
+ function isSafeRedirectPath(value) {
31938
+ if (new TextEncoder().encode(value).byteLength > MAX_REDIRECT_BYTES)
31939
+ return false;
31940
+ if (!isSafeCandidate(value))
31941
+ return false;
31942
+ let decoded = value;
31943
+ for (let depth = 0;depth < 8; depth += 1) {
31944
+ let next;
31945
+ try {
31946
+ next = decodeURIComponent(decoded);
31947
+ } catch {
31948
+ return depth > 0;
31949
+ }
31950
+ if (next === decoded)
31951
+ break;
31952
+ if (!isSafeCandidate(next))
31953
+ return false;
31954
+ decoded = next;
31955
+ if (depth === 7)
31956
+ return false;
31957
+ }
31958
+ try {
31959
+ const base = new URL("https://alook.invalid");
31960
+ const resolved = new URL(value, base);
31961
+ return resolved.origin === base.origin;
31962
+ } catch {
31963
+ return false;
31964
+ }
31965
+ }
31966
+
31967
+ // ../shared/src/lib/native-oauth-contract.ts
31968
+ var ATTEMPT_ID = /^[A-Za-z0-9_-]{22,64}$/;
31969
+ var SHA256_HEX = /^[0-9a-f]{64}$/;
31970
+ var BASE64URL_32_BYTES = /^[A-Za-z0-9_-]{43}$/;
31971
+ var HANDOFF_CODE = /^[A-Za-z0-9_-]{32,128}$/;
31972
+ var nativeOauthProviderSchema = exports_external.enum(["github", "google"]);
31973
+ var nativeOauthPlatformSchema = exports_external.enum([
31974
+ "macos",
31975
+ "windows",
31976
+ "linux",
31977
+ "ios",
31978
+ "android"
31979
+ ]);
31980
+ var nativeOauthAttemptIdSchema = exports_external.string().regex(ATTEMPT_ID);
31981
+ var nativeOauthHandoffCodeSchema = exports_external.string().regex(HANDOFF_CODE);
31982
+ var nativeOauthFailureCodeSchema = exports_external.enum([
31983
+ "access_denied",
31984
+ "provider_error",
31985
+ "oauth_callback_failed",
31986
+ "start_failed",
31987
+ "invalid_handoff"
31988
+ ]);
31989
+ var nativeOauthRegistrationSchema = exports_external.object({
31990
+ attemptId: nativeOauthAttemptIdSchema,
31991
+ stateHash: exports_external.string().regex(SHA256_HEX),
31992
+ codeChallenge: exports_external.string().regex(BASE64URL_32_BYTES),
31993
+ instanceKeyHash: exports_external.string().regex(SHA256_HEX),
31994
+ platform: nativeOauthPlatformSchema,
31995
+ provider: nativeOauthProviderSchema,
31996
+ redirectPath: exports_external.string().refine(isSafeRedirectPath)
31997
+ }).strict();
31998
+ var nativeOauthExchangeSchema = exports_external.object({
31999
+ attemptId: nativeOauthAttemptIdSchema,
32000
+ state: exports_external.string().regex(BASE64URL_32_BYTES),
32001
+ verifier: exports_external.string().regex(BASE64URL_32_BYTES),
32002
+ code: nativeOauthHandoffCodeSchema
32003
+ }).strict();
32004
+ var nativeOauthProofSchema = nativeOauthExchangeSchema.omit({ code: true }).strict();
31036
32005
  // src/manager/wakeCoordinator.ts
31037
32006
  class WakeCoordinator {
31038
32007
  agents = new Map;
@@ -31823,11 +32792,11 @@ function timelineRowHash(line) {
31823
32792
  }
31824
32793
  function timelineFileGeneration(filePath, lines) {
31825
32794
  try {
31826
- const stat = fs7.lstatSync(filePath, { bigint: true });
31827
- if (!stat.isFile())
32795
+ const stat2 = fs7.lstatSync(filePath, { bigint: true });
32796
+ if (!stat2.isFile())
31828
32797
  return null;
31829
32798
  const digest = createHash4("sha256");
31830
- digest.update(`${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}
32799
+ digest.update(`${stat2.dev}:${stat2.ino}:${stat2.size}:${stat2.mtimeNs}:${stat2.ctimeNs}
31831
32800
  `);
31832
32801
  for (const line of lines)
31833
32802
  digest.update(line.text, "utf8").update(`
@@ -31885,9 +32854,9 @@ function compactLines(input) {
31885
32854
  }
31886
32855
  return suffix;
31887
32856
  }
31888
- function isRealDirectory(path9) {
32857
+ function isRealDirectory(path13) {
31889
32858
  try {
31890
- return fs7.lstatSync(path9).isDirectory();
32859
+ return fs7.lstatSync(path13).isDirectory();
31891
32860
  } catch {
31892
32861
  return false;
31893
32862
  }
@@ -31927,8 +32896,8 @@ function scanTimelineFile(filePath) {
31927
32896
  let fd = null;
31928
32897
  try {
31929
32898
  fd = fs7.openSync(filePath, fs7.constants.O_RDONLY | (fs7.constants.O_NOFOLLOW ?? 0));
31930
- const stat = fs7.fstatSync(fd);
31931
- if (!stat.isFile())
32899
+ const stat2 = fs7.fstatSync(fd);
32900
+ if (!stat2.isFile())
31932
32901
  return null;
31933
32902
  const chunk2 = Buffer.allocUnsafe(TIMELINE_READ_CHUNK_BYTES);
31934
32903
  const newest = [];
@@ -31941,9 +32910,9 @@ function scanTimelineFile(filePath) {
31941
32910
  let oversized = false;
31942
32911
  let stop = false;
31943
32912
  let discardIncompleteTail = false;
31944
- if (stat.size > 0) {
32913
+ if (stat2.size > 0) {
31945
32914
  const last = Buffer.allocUnsafe(1);
31946
- fs7.readSync(fd, last, 0, 1, stat.size - 1);
32915
+ fs7.readSync(fd, last, 0, 1, stat2.size - 1);
31947
32916
  discardIncompleteTail = last[0] !== 10;
31948
32917
  }
31949
32918
  const resetPhysicalLine = () => {
@@ -32002,7 +32971,7 @@ function scanTimelineFile(filePath) {
32002
32971
  }
32003
32972
  resetPhysicalLine();
32004
32973
  };
32005
- let position = stat.size;
32974
+ let position = stat2.size;
32006
32975
  while (position > 0 && !stop) {
32007
32976
  const start = Math.max(0, position - chunk2.length);
32008
32977
  const requested = position - start;
@@ -32154,11 +33123,11 @@ function readResumeControlState(timelineDir) {
32154
33123
  let fd = null;
32155
33124
  try {
32156
33125
  fd = fs7.openSync(filePath, fs7.constants.O_RDONLY | (fs7.constants.O_NOFOLLOW ?? 0));
32157
- const stat = fs7.fstatSync(fd);
32158
- if (!stat.isFile() || stat.size <= 0 || stat.size > RESUME_CONTROL_MAX_BYTES) {
33126
+ const stat2 = fs7.fstatSync(fd);
33127
+ if (!stat2.isFile() || stat2.size <= 0 || stat2.size > RESUME_CONTROL_MAX_BYTES) {
32159
33128
  return { kind: "invalid" };
32160
33129
  }
32161
- const bounded = Buffer.allocUnsafe(stat.size + 1);
33130
+ const bounded = Buffer.allocUnsafe(stat2.size + 1);
32162
33131
  let bytesRead = 0;
32163
33132
  while (bytesRead < bounded.length) {
32164
33133
  const count = fs7.readSync(fd, bounded, bytesRead, bounded.length - bytesRead, bytesRead);
@@ -32166,7 +33135,7 @@ function readResumeControlState(timelineDir) {
32166
33135
  break;
32167
33136
  bytesRead += count;
32168
33137
  }
32169
- if (bytesRead !== stat.size)
33138
+ if (bytesRead !== stat2.size)
32170
33139
  return { kind: "invalid" };
32171
33140
  const raw = bounded.subarray(0, bytesRead).toString("utf8");
32172
33141
  const value = JSON.parse(raw);
@@ -32953,26 +33922,26 @@ function createTimelineRecorder(opts) {
32953
33922
  }
32954
33923
  }
32955
33924
  // src/discovery.ts
32956
- import * as path9 from "path";
33925
+ import * as path13 from "path";
32957
33926
  import * as fs8 from "fs";
32958
33927
  import { fileURLToPath } from "url";
32959
33928
  function resolveAlookCliPath(moduleDir) {
32960
- const thisDir = moduleDir ?? path9.dirname(fileURLToPath(import.meta.url));
32961
- const target2 = path9.basename(thisDir) === "dist" ? path9.resolve(thisDir, "cli", "index.js") : path9.resolve(thisDir, "..", "scripts", "alook-shim.mjs");
33929
+ const thisDir = moduleDir ?? path13.dirname(fileURLToPath(import.meta.url));
33930
+ const target2 = path13.basename(thisDir) === "dist" ? path13.resolve(thisDir, "cli", "index.js") : path13.resolve(thisDir, "..", "scripts", "alook-shim.mjs");
32962
33931
  return fs8.existsSync(target2) ? target2 : null;
32963
33932
  }
32964
33933
  function deriveCliFallbackCandidates(cliPath) {
32965
33934
  if (!cliPath)
32966
33935
  return [];
32967
- const normalized = cliPath.split(path9.sep).join("/");
33936
+ const normalized = cliPath.split(path13.sep).join("/");
32968
33937
  const marker = "/node_modules/";
32969
33938
  const idx = normalized.indexOf(marker);
32970
33939
  if (idx === -1)
32971
33940
  return [];
32972
33941
  const globalRoot = cliPath.slice(0, idx + marker.length - 1);
32973
- const tail = path9.join("dist", "cli", "index.js");
33942
+ const tail = path13.join("dist", "cli", "index.js");
32974
33943
  return [
32975
- path9.join(globalRoot, "@alook", "daemon", tail)
33944
+ path13.join(globalRoot, "@alook", "daemon", tail)
32976
33945
  ].filter((candidate) => candidate !== cliPath);
32977
33946
  }
32978
33947
  function resolveAlookCliPathWithFallback(primary) {
@@ -33167,7 +34136,7 @@ class MessageReminderScheduler {
33167
34136
 
33168
34137
  // src/manager/agentDriverHost.ts
33169
34138
  import { randomUUID as randomUUID7 } from "node:crypto";
33170
- import { homedir as homedir4 } from "node:os";
34139
+ import { homedir as homedir6 } from "node:os";
33171
34140
  import { join as join13 } from "node:path";
33172
34141
 
33173
34142
  // src/drivers/gitIdentityEnv.ts
@@ -33293,7 +34262,7 @@ function createDaemonAgentDriverHost(ctx, onRawLine) {
33293
34262
  hostUser: readHostGitIdentity() ?? undefined
33294
34263
  }),
33295
34264
  platformProtected: {
33296
- ALOOK_HOME: process.env.ALOOK_HOME ?? join13(homedir4(), ".alook"),
34265
+ ALOOK_HOME: process.env.ALOOK_HOME ?? join13(homedir6(), ".alook"),
33297
34266
  ALOOK_ID: ctx.agentId,
33298
34267
  ALOOK_CLI: ctx.agentCliPath,
33299
34268
  ALOOK_SERVER_URL: ctx.config.serverUrl,
@@ -33614,13 +34583,13 @@ function createRuntimeRawLineTap(args) {
33614
34583
  const maxBytes = args.maxBytes ?? RUNTIME_RAW_TRACE_MAX_BYTES;
33615
34584
  const sinks = new Map;
33616
34585
  const warned = new Set;
33617
- const warnOnce = (agentId, path10, operation, error51) => {
34586
+ const warnOnce = (agentId, path14, operation, error51) => {
33618
34587
  if (warned.has(agentId))
33619
34588
  return;
33620
34589
  warned.add(agentId);
33621
34590
  args.logger.warn("runtime raw trace sink failed", {
33622
34591
  agentId,
33623
- path: path10,
34592
+ path: path14,
33624
34593
  operation,
33625
34594
  error: error51 instanceof Error ? error51.message : String(error51)
33626
34595
  });
@@ -33629,10 +34598,10 @@ function createRuntimeRawLineTap(args) {
33629
34598
  if (!args.enabledAgentIds.has(agentId))
33630
34599
  return;
33631
34600
  const encodedAgentId = encodeURIComponent(agentId).replace(/\./g, "%2E");
33632
- const path10 = `${traceDir}/runtime-raw-events-${encodedAgentId}.jsonl`;
34601
+ const path14 = `${traceDir}/runtime-raw-events-${encodedAgentId}.jsonl`;
33633
34602
  const serializedBytes = Buffer.byteLength(line, "utf8") + 1;
33634
34603
  if (serializedBytes > maxBytes) {
33635
- warnOnce(agentId, path10, "oversize", new Error(`raw line is ${serializedBytes} bytes; max is ${maxBytes}`));
34604
+ warnOnce(agentId, path14, "oversize", new Error(`raw line is ${serializedBytes} bytes; max is ${maxBytes}`));
33636
34605
  return;
33637
34606
  }
33638
34607
  let sink = sinks.get(agentId);
@@ -33640,12 +34609,12 @@ function createRuntimeRawLineTap(args) {
33640
34609
  try {
33641
34610
  mkdirSync7(traceDir, { recursive: true });
33642
34611
  } catch (error51) {
33643
- warnOnce(agentId, path10, "mkdir", error51);
34612
+ warnOnce(agentId, path14, "mkdir", error51);
33644
34613
  }
33645
- sink = createRotatingFileSink(path10, maxBytes, {
34614
+ sink = createRotatingFileSink(path14, maxBytes, {
33646
34615
  mode: 384,
33647
34616
  hardMaxBytes: true,
33648
- onError: ({ operation, error: error51 }) => warnOnce(agentId, path10, operation, error51)
34617
+ onError: ({ operation, error: error51 }) => warnOnce(agentId, path14, operation, error51)
33649
34618
  });
33650
34619
  sinks.set(agentId, sink);
33651
34620
  }
@@ -33737,7 +34706,7 @@ function createBuiltinDaemonSessionFactory(onRuntimeRawLine) {
33737
34706
  }
33738
34707
  async function createDaemon(opts) {
33739
34708
  const log2 = opts.logger ?? createLogger({ header: "@alook/daemon" });
33740
- const fallbackBase = (process.env.ALOOK_PROJECT_ROOT || `${homedir5()}/.alook`) + "/daemon";
34709
+ const fallbackBase = (process.env.ALOOK_PROJECT_ROOT || `${homedir7()}/.alook`) + "/daemon";
33741
34710
  const workingDirectoryBase = opts.workingDirectoryBase ?? fallbackBase;
33742
34711
  const workdirFor = (agentId) => `${workingDirectoryBase}/${agentId}`;
33743
34712
  const dailyTokenUsage2 = new DailyTokenUsageStore(workingDirectoryBase);
@@ -33816,19 +34785,24 @@ async function createDaemon(opts) {
33816
34785
  const broker = new CredentialBroker({ upstreamBaseUrl: opts.serverUrl });
33817
34786
  const proxy = await startCredentialProxy(broker, {
33818
34787
  onInboxPullStart: (agentId) => ({
33819
- modelSeenGeneration: channelRef?.modelSeenGeneration(agentId),
33820
34788
  owner: managerRef?.timelineTurnOwner(agentId) ?? null
33821
34789
  }),
33822
34790
  onInboxPullResponse: (agentId, messages, observationToken) => {
33823
34791
  const token = observationToken && typeof observationToken === "object" ? observationToken : null;
33824
34792
  timeline2.recordInboxPull(agentId, token?.owner ?? null, messages);
33825
- if (typeof token?.modelSeenGeneration === "number") {
33826
- channelRef?.recordModelSeen(agentId, messages, token.modelSeenGeneration);
33827
- }
33828
34793
  },
33829
34794
  onInboxPullObservationError: ({ agentId, reason, contentEncoding }) => {
33830
34795
  log2.warn("inbox pull timeline observation failed", { agentId, reason, contentEncoding });
33831
34796
  },
34797
+ onInboxAckStart: (agentId) => channelRef?.modelSeenGeneration(agentId),
34798
+ onInboxAckResponse: (agentId, applied, observationToken) => {
34799
+ if (typeof observationToken !== "number")
34800
+ return;
34801
+ channelRef?.recordModelSeen(agentId, applied.map((cursor) => ({ channel: cursor.channel, seq: `#${cursor.seq}` })), observationToken);
34802
+ },
34803
+ onInboxAckObservationError: ({ agentId, reason, contentEncoding }) => {
34804
+ log2.warn("inbox ack observation failed", { agentId, reason, contentEncoding });
34805
+ },
33832
34806
  onMessageReminderArm: (input) => reminderSchedulerRef?.arm(input) ?? { armed: false, reason: "reminder_scheduler_unavailable" },
33833
34807
  onProxyRequest: (agentId, method, pathname, metadata) => {
33834
34808
  const subcommand = deriveAuditLogSubcommand(pathname, method);
@@ -34328,6 +35302,7 @@ export {
34328
35302
  buildApmFreshnessDecisionProducerFactId,
34329
35303
  UnknownRuntimeError,
34330
35304
  UnknownBotError,
35305
+ SESSION_FILE_DISCOVERY_CAPABILITIES2 as SESSION_FILE_DISCOVERY_CAPABILITIES,
34331
35306
  RuntimeProgressState,
34332
35307
  RuntimeNotificationState,
34333
35308
  RUNTIME_RAW_TRACE_AGENT_IDS_ENV,