hoomanjs 1.58.0 → 1.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/acp/acp-agent.js +17 -3
  2. package/dist/acp/acp-agent.js.map +1 -1
  3. package/dist/acp/approvals.js +11 -0
  4. package/dist/acp/approvals.js.map +1 -1
  5. package/dist/acp/meta/daemon.d.ts +12 -0
  6. package/dist/acp/meta/daemon.js +16 -0
  7. package/dist/acp/meta/daemon.js.map +1 -0
  8. package/dist/acp/meta/origin.d.ts +8 -0
  9. package/dist/acp/meta/origin.js +38 -0
  10. package/dist/acp/meta/origin.js.map +1 -0
  11. package/dist/acp/sessions/store.d.ts +6 -0
  12. package/dist/acp/sessions/store.js.map +1 -1
  13. package/dist/cli.js +78 -26
  14. package/dist/cli.js.map +1 -1
  15. package/dist/configure/app.js +1 -0
  16. package/dist/configure/app.js.map +1 -1
  17. package/dist/core/config.d.ts +29 -0
  18. package/dist/core/config.js +32 -0
  19. package/dist/core/config.js.map +1 -1
  20. package/dist/core/index.d.ts +7 -0
  21. package/dist/core/index.js +2 -1
  22. package/dist/core/index.js.map +1 -1
  23. package/dist/core/mcp/manager.d.ts +15 -0
  24. package/dist/core/mcp/manager.js +28 -0
  25. package/dist/core/mcp/manager.js.map +1 -1
  26. package/dist/core/mcp/prefixed-mcp-tool.d.ts +1 -0
  27. package/dist/core/mcp/prefixed-mcp-tool.js +1 -1
  28. package/dist/core/mcp/prefixed-mcp-tool.js.map +1 -1
  29. package/dist/core/skills/built-in/hooman-config/SKILL.md +35 -3
  30. package/dist/daemon/acp-client.d.ts +49 -0
  31. package/dist/daemon/acp-client.js +130 -0
  32. package/dist/daemon/acp-client.js.map +1 -0
  33. package/dist/daemon/approvals.d.ts +9 -2
  34. package/dist/daemon/approvals.js +151 -6
  35. package/dist/daemon/approvals.js.map +1 -1
  36. package/dist/daemon/attachments.d.ts +9 -0
  37. package/dist/daemon/attachments.js +63 -0
  38. package/dist/daemon/attachments.js.map +1 -0
  39. package/dist/daemon/index.d.ts +16 -6
  40. package/dist/daemon/index.js +162 -71
  41. package/dist/daemon/index.js.map +1 -1
  42. package/dist/daemon/mcproxy/catalog.d.ts +17 -0
  43. package/dist/daemon/mcproxy/catalog.js +71 -0
  44. package/dist/daemon/mcproxy/catalog.js.map +1 -0
  45. package/dist/daemon/mcproxy/http-server.d.ts +15 -0
  46. package/dist/daemon/mcproxy/http-server.js +150 -0
  47. package/dist/daemon/mcproxy/http-server.js.map +1 -0
  48. package/dist/daemon/mcproxy/index.d.ts +23 -0
  49. package/dist/daemon/mcproxy/index.js +23 -0
  50. package/dist/daemon/mcproxy/index.js.map +1 -0
  51. package/dist/daemon/mcproxy/session.d.ts +9 -0
  52. package/dist/daemon/mcproxy/session.js +24 -0
  53. package/dist/daemon/mcproxy/session.js.map +1 -0
  54. package/dist/daemon/queue.d.ts +14 -5
  55. package/dist/daemon/queue.js +31 -33
  56. package/dist/daemon/queue.js.map +1 -1
  57. package/dist/daemon/session-registry.d.ts +66 -0
  58. package/dist/daemon/session-registry.js +198 -0
  59. package/dist/daemon/session-registry.js.map +1 -0
  60. package/dist/daemon/session-store.d.ts +20 -0
  61. package/dist/daemon/session-store.js +63 -0
  62. package/dist/daemon/session-store.js.map +1 -0
  63. package/package.json +1 -1
  64. package/dist/daemon/questions.d.ts +0 -18
  65. package/dist/daemon/questions.js +0 -44
  66. package/dist/daemon/questions.js.map +0 -1
@@ -0,0 +1,9 @@
1
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
+ import type { DaemonToolCatalog } from "./catalog.js";
3
+ /**
4
+ * One frontend MCP session for a daemon-hosted ACP child: a fresh low-level
5
+ * `Server` per HTTP initialize handshake, exposing only the aggregate tool
6
+ * catalog (no experimental channel capabilities — those stay on the parent's
7
+ * singleton upstream connections).
8
+ */
9
+ export declare function createDaemonProxyServer(catalog: DaemonToolCatalog, instructions: string): Server;
@@ -0,0 +1,24 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
4
+ const packageUrl = new URL("../../../package.json", import.meta.url);
5
+ const packageVersion = JSON.parse(readFileSync(packageUrl, "utf8"))
6
+ .version ?? "0.0.0";
7
+ /**
8
+ * One frontend MCP session for a daemon-hosted ACP child: a fresh low-level
9
+ * `Server` per HTTP initialize handshake, exposing only the aggregate tool
10
+ * catalog (no experimental channel capabilities — those stay on the parent's
11
+ * singleton upstream connections).
12
+ */
13
+ export function createDaemonProxyServer(catalog, instructions) {
14
+ const server = new Server({ name: "hooman-daemon-mcproxy", version: packageVersion }, {
15
+ capabilities: { tools: {} },
16
+ instructions: instructions.length > 0 ? instructions : undefined,
17
+ });
18
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
19
+ tools: await catalog.listTools(),
20
+ }));
21
+ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => catalog.callTool(request.params.name, request.params.arguments, extra.signal));
22
+ return server;
23
+ }
24
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/daemon/mcproxy/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,oCAAoC,CAAC;AAG5C,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,uBAAuB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACrE,MAAM,cAAc,GACjB,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAA0B;KACnE,OAAO,IAAI,OAAO,CAAC;AAExB;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,OAA0B,EAC1B,YAAoB;IAEpB,MAAM,MAAM,GAAG,IAAI,MAAM,CACvB,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,cAAc,EAAE,EAC1D;QACE,YAAY,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;QAC3B,YAAY,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS;KACjE,CACF,CAAC;IACF,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QAC5D,KAAK,EAAE,MAAM,OAAO,CAAC,SAAS,EAAE;KACjC,CAAC,CAAC,CAAC;IACJ,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CACvE,OAAO,CAAC,QAAQ,CACd,OAAO,CAAC,MAAM,CAAC,IAAI,EACnB,OAAO,CAAC,MAAM,CAAC,SAAS,EACxB,KAAK,CAAC,MAAM,CACb,CACF,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -1,5 +1,14 @@
1
- import fastq from "fastq";
2
- import type { ChannelMessage } from "../core/mcp/index.js";
3
- type MessageQueue = fastq.queueAsPromised<ChannelMessage, void>;
4
- export declare function createQueue(handler: (message: ChannelMessage) => Promise<void>, cleanup: () => void): Promise<[MessageQueue, () => Promise<void>, () => void]>;
5
- export {};
1
+ /**
2
+ * Serializes turns per external conversation key while allowing unrelated
3
+ * keys to run fully concurrently — replaces the old single global-concurrency
4
+ * queue now that many ACP sessions may be active at once.
5
+ */
6
+ export declare class KeyedTurnQueue {
7
+ #private;
8
+ /** Number of tasks currently queued (including the one running, if any) for `key`. */
9
+ length(key: string): number;
10
+ /** Enqueues `task` behind any already-running/queued task for the same `key`. */
11
+ push(key: string, task: () => Promise<void>): void;
12
+ /** Waits for every currently queued task (across every key) to settle. */
13
+ drain(): Promise<void>;
14
+ }
@@ -1,39 +1,37 @@
1
- import fastq from "fastq";
2
- export async function createQueue(handler, cleanup) {
3
- let stopping = false;
4
- let resolver = null;
5
- const queue = fastq.promise(async (message) => {
6
- await handler(message);
7
- }, 1);
8
- const stopper = new Promise((resolve) => {
9
- resolver = resolve;
10
- });
11
- const shutdown = () => {
12
- if (stopping) {
13
- return;
14
- }
15
- stopping = true;
16
- queue.kill();
17
- resolver?.();
18
- };
19
- const onSigInt = () => shutdown();
20
- const onSigTerm = () => shutdown();
21
- process.on("SIGINT", onSigInt);
22
- process.on("SIGTERM", onSigTerm);
23
- return [
24
- queue,
25
- async () => {
1
+ /**
2
+ * Serializes turns per external conversation key while allowing unrelated
3
+ * keys to run fully concurrently — replaces the old single global-concurrency
4
+ * queue now that many ACP sessions may be active at once.
5
+ */
6
+ export class KeyedTurnQueue {
7
+ #tails = new Map();
8
+ /** Number of tasks currently queued (including the one running, if any) for `key`. */
9
+ length(key) {
10
+ return this.#tails.get(key)?.length ?? 0;
11
+ }
12
+ /** Enqueues `task` behind any already-running/queued task for the same `key`. */
13
+ push(key, task) {
14
+ const state = this.#tails.get(key) ?? {
15
+ length: 0,
16
+ tail: Promise.resolve(),
17
+ };
18
+ state.length += 1;
19
+ this.#tails.set(key, state);
20
+ state.tail = state.tail.then(async () => {
26
21
  try {
27
- await stopper;
22
+ await task();
28
23
  }
29
24
  finally {
30
- cleanup();
31
- await queue.drained().catch(() => { });
32
- process.off("SIGINT", onSigInt);
33
- process.off("SIGTERM", onSigTerm);
25
+ state.length -= 1;
26
+ if (state.length === 0) {
27
+ this.#tails.delete(key);
28
+ }
34
29
  }
35
- },
36
- shutdown,
37
- ];
30
+ });
31
+ }
32
+ /** Waits for every currently queued task (across every key) to settle. */
33
+ async drain() {
34
+ await Promise.allSettled([...this.#tails.values()].map((s) => s.tail));
35
+ }
38
36
  }
39
37
  //# sourceMappingURL=queue.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/daemon/queue.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,OAAmD,EACnD,OAAmB;IAEnB,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,QAAQ,GAAwB,IAAI,CAAC;IACzC,MAAM,KAAK,GAAiB,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,OAAuB,EAAE,EAAE;QAC1E,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC,EAAE,CAAC,CAAC,CAAC;IAEN,MAAM,OAAO,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QAC5C,QAAQ,GAAG,OAAO,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,GAAG,EAAE;QACpB,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QACD,QAAQ,GAAG,IAAI,CAAC;QAChB,KAAK,CAAC,IAAI,EAAE,CAAC;QACb,QAAQ,EAAE,EAAE,CAAC;IACf,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IAClC,MAAM,SAAS,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IAEnC,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAEjC,OAAO;QACL,KAAK;QACL,KAAK,IAAI,EAAE;YACT,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC;YAChB,CAAC;oBAAS,CAAC;gBACT,OAAO,EAAE,CAAC;gBACV,MAAM,KAAK,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;gBACtC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;gBAChC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;QACD,QAAQ;KACT,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../src/daemon/queue.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,OAAO,cAAc;IACzB,MAAM,GAAG,IAAI,GAAG,EAAmD,CAAC;IAEpE,sFAAsF;IAC/E,MAAM,CAAC,GAAW;QACvB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;IAC3C,CAAC;IAED,iFAAiF;IAC1E,IAAI,CAAC,GAAW,EAAE,IAAyB;QAChD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI;YACpC,MAAM,EAAE,CAAC;YACT,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE;SACxB,CAAC;QACF,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;QAClB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC5B,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE;YACtC,IAAI,CAAC;gBACH,MAAM,IAAI,EAAE,CAAC;YACf,CAAC;oBAAS,CAAC;gBACT,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;gBAClB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACvB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,0EAA0E;IACnE,KAAK,CAAC,KAAK;QAChB,MAAM,OAAO,CAAC,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACzE,CAAC;CACF"}
@@ -0,0 +1,66 @@
1
+ import type { ChannelOrigin } from "../core/approvals/channel-ask.js";
2
+ export type DaemonSessionRegistryOptions = {
3
+ /** Bound on concurrently active ACP sessions. Must be a positive integer. */
4
+ maxActiveSessions: number;
5
+ /** Ordinary idle-close delay after a session becomes non-busy. `0` disables it (pressure eviction still applies). */
6
+ idleTimeoutMs: number;
7
+ /** Called to close an ACP session (idle timeout, pool pressure, or shutdown). Must not throw. */
8
+ onClose: (externalKey: string, acpSessionId: string) => Promise<void>;
9
+ };
10
+ /**
11
+ * Tracks the daemon's channel-conversation → ACP-session bindings, the
12
+ * durable JSONL mapping, and a bounded pool of concurrently active ACP
13
+ * sessions: idle sessions are evicted LRU under pressure, callers otherwise
14
+ * wait FIFO for a slot, and a non-busy session with pool waiters closes
15
+ * immediately instead of waiting out the ordinary idle timeout.
16
+ */
17
+ export declare class DaemonSessionRegistry {
18
+ #private;
19
+ private readonly options;
20
+ constructor(options: DaemonSessionRegistryOptions);
21
+ hydrate(): Promise<void>;
22
+ /** Persisted ACP session ID for a key from a prior daemon run, if any. */
23
+ persistedAcpSessionId(externalKey: string): string | undefined;
24
+ isActive(externalKey: string): boolean;
25
+ /** Whether an ACP session is already registered (created/resumed) for `externalKey` in this process. */
26
+ hasRuntime(externalKey: string): boolean;
27
+ acpSessionIdFor(externalKey: string): string | undefined;
28
+ /**
29
+ * Acquires one pool slot for `externalKey`. Reuses the caller's own slot
30
+ * if already held. Otherwise joins the FIFO waiter queue and, if a slot
31
+ * exists, immediately requests one idle LRU session be evicted to make
32
+ * room — but never grants the freed slot directly, so fairness holds even
33
+ * when eviction and waiting happen concurrently.
34
+ */
35
+ acquireSlot(externalKey: string): Promise<void>;
36
+ /** Registers a newly created/resumed active session and marks it busy for the in-flight turn. */
37
+ registerActive(binding: {
38
+ externalKey: string;
39
+ acpSessionId: string;
40
+ cwd: string;
41
+ userId?: string;
42
+ origin: ChannelOrigin | null;
43
+ }): void;
44
+ /** Persists the external-key → ACP-session-ID binding (create, or replacement after a missing-session resume failure). */
45
+ persistBinding(params: {
46
+ externalKey: string;
47
+ acpSessionId: string;
48
+ cwd: string;
49
+ userId?: string | null;
50
+ }): Promise<void>;
51
+ updateOrigin(externalKey: string, origin: ChannelOrigin | null): void;
52
+ originForAcpSession(acpSessionId: string): ChannelOrigin | null;
53
+ externalKeyForAcpSession(acpSessionId: string): string | undefined;
54
+ /** Marks a session busy for the duration of one `session/prompt` call, cancelling any armed idle timer. */
55
+ markBusy(externalKey: string): void;
56
+ /**
57
+ * Marks a session non-busy after its turn settles. When more work for the
58
+ * same key is already queued, the caller keeps its slot untouched. When
59
+ * pool waiters exist, the session closes immediately instead of arming the
60
+ * ordinary idle timer.
61
+ */
62
+ markIdle(externalKey: string, hasQueuedWork: boolean): void;
63
+ /** Rejects queued waiters, closes every active session, and stops granting new slots. */
64
+ shutdown(): Promise<void>;
65
+ get isShuttingDown(): boolean;
66
+ }
@@ -0,0 +1,198 @@
1
+ import { patchDaemonSessionBinding, readDaemonSessionBindings, } from "./session-store.js";
2
+ /**
3
+ * Tracks the daemon's channel-conversation → ACP-session bindings, the
4
+ * durable JSONL mapping, and a bounded pool of concurrently active ACP
5
+ * sessions: idle sessions are evicted LRU under pressure, callers otherwise
6
+ * wait FIFO for a slot, and a non-busy session with pool waiters closes
7
+ * immediately instead of waiting out the ordinary idle timeout.
8
+ */
9
+ export class DaemonSessionRegistry {
10
+ options;
11
+ #persisted = new Map();
12
+ #runtime = new Map();
13
+ #reverse = new Map();
14
+ #activeKeys = new Set();
15
+ #waiters = [];
16
+ #shuttingDown = false;
17
+ constructor(options) {
18
+ this.options = options;
19
+ if (!Number.isInteger(options.maxActiveSessions) ||
20
+ options.maxActiveSessions < 1) {
21
+ throw new Error("maxActiveSessions must be a positive integer.");
22
+ }
23
+ }
24
+ async hydrate() {
25
+ this.#persisted = await readDaemonSessionBindings();
26
+ }
27
+ /** Persisted ACP session ID for a key from a prior daemon run, if any. */
28
+ persistedAcpSessionId(externalKey) {
29
+ return this.#persisted.get(externalKey)?.acpSessionId;
30
+ }
31
+ isActive(externalKey) {
32
+ return this.#activeKeys.has(externalKey);
33
+ }
34
+ /** Whether an ACP session is already registered (created/resumed) for `externalKey` in this process. */
35
+ hasRuntime(externalKey) {
36
+ return this.#runtime.has(externalKey);
37
+ }
38
+ acpSessionIdFor(externalKey) {
39
+ return this.#runtime.get(externalKey)?.acpSessionId;
40
+ }
41
+ /**
42
+ * Acquires one pool slot for `externalKey`. Reuses the caller's own slot
43
+ * if already held. Otherwise joins the FIFO waiter queue and, if a slot
44
+ * exists, immediately requests one idle LRU session be evicted to make
45
+ * room — but never grants the freed slot directly, so fairness holds even
46
+ * when eviction and waiting happen concurrently.
47
+ */
48
+ async acquireSlot(externalKey) {
49
+ if (this.#activeKeys.has(externalKey)) {
50
+ return;
51
+ }
52
+ if (this.#activeKeys.size < this.options.maxActiveSessions) {
53
+ this.#activeKeys.add(externalKey);
54
+ return;
55
+ }
56
+ const waiterPromise = new Promise((resolve) => {
57
+ this.#waiters.push({ key: externalKey, resolve });
58
+ });
59
+ const idleKey = this.#pickIdleLru();
60
+ if (idleKey) {
61
+ void this.#closeRuntime(idleKey);
62
+ }
63
+ await waiterPromise;
64
+ }
65
+ /** Registers a newly created/resumed active session and marks it busy for the in-flight turn. */
66
+ registerActive(binding) {
67
+ this.#runtime.set(binding.externalKey, {
68
+ externalKey: binding.externalKey,
69
+ acpSessionId: binding.acpSessionId,
70
+ cwd: binding.cwd,
71
+ userId: binding.userId,
72
+ origin: binding.origin,
73
+ busy: true,
74
+ closing: false,
75
+ lastActiveAt: Date.now(),
76
+ idleTimer: null,
77
+ });
78
+ this.#reverse.set(binding.acpSessionId, binding.externalKey);
79
+ }
80
+ /** Persists the external-key → ACP-session-ID binding (create, or replacement after a missing-session resume failure). */
81
+ async persistBinding(params) {
82
+ const now = new Date().toISOString();
83
+ const existing = this.#persisted.get(params.externalKey);
84
+ const entry = {
85
+ externalKey: params.externalKey,
86
+ acpSessionId: params.acpSessionId,
87
+ cwd: params.cwd,
88
+ userId: params.userId ?? null,
89
+ createdAt: existing?.createdAt ?? now,
90
+ updatedAt: now,
91
+ };
92
+ this.#persisted.set(params.externalKey, entry);
93
+ await patchDaemonSessionBinding(entry);
94
+ }
95
+ updateOrigin(externalKey, origin) {
96
+ const runtime = this.#runtime.get(externalKey);
97
+ if (runtime) {
98
+ runtime.origin = origin;
99
+ }
100
+ }
101
+ originForAcpSession(acpSessionId) {
102
+ const key = this.#reverse.get(acpSessionId);
103
+ if (!key) {
104
+ return null;
105
+ }
106
+ return this.#runtime.get(key)?.origin ?? null;
107
+ }
108
+ externalKeyForAcpSession(acpSessionId) {
109
+ return this.#reverse.get(acpSessionId);
110
+ }
111
+ /** Marks a session busy for the duration of one `session/prompt` call, cancelling any armed idle timer. */
112
+ markBusy(externalKey) {
113
+ const runtime = this.#runtime.get(externalKey);
114
+ if (!runtime) {
115
+ return;
116
+ }
117
+ runtime.busy = true;
118
+ if (runtime.idleTimer) {
119
+ clearTimeout(runtime.idleTimer);
120
+ runtime.idleTimer = null;
121
+ }
122
+ }
123
+ /**
124
+ * Marks a session non-busy after its turn settles. When more work for the
125
+ * same key is already queued, the caller keeps its slot untouched. When
126
+ * pool waiters exist, the session closes immediately instead of arming the
127
+ * ordinary idle timer.
128
+ */
129
+ markIdle(externalKey, hasQueuedWork) {
130
+ const runtime = this.#runtime.get(externalKey);
131
+ if (!runtime || runtime.closing) {
132
+ return;
133
+ }
134
+ runtime.busy = false;
135
+ runtime.lastActiveAt = Date.now();
136
+ if (hasQueuedWork) {
137
+ return;
138
+ }
139
+ if (this.#waiters.length > 0) {
140
+ void this.#closeRuntime(externalKey);
141
+ return;
142
+ }
143
+ if (this.options.idleTimeoutMs > 0) {
144
+ runtime.idleTimer = setTimeout(() => {
145
+ runtime.idleTimer = null;
146
+ if (!runtime.busy && !runtime.closing) {
147
+ void this.#closeRuntime(externalKey);
148
+ }
149
+ }, this.options.idleTimeoutMs);
150
+ runtime.idleTimer.unref?.();
151
+ }
152
+ }
153
+ #pickIdleLru() {
154
+ let best;
155
+ for (const runtime of this.#runtime.values()) {
156
+ if (runtime.busy || runtime.closing) {
157
+ continue;
158
+ }
159
+ if (!best || runtime.lastActiveAt < best.lastActiveAt) {
160
+ best = runtime;
161
+ }
162
+ }
163
+ return best?.externalKey;
164
+ }
165
+ async #closeRuntime(externalKey) {
166
+ const runtime = this.#runtime.get(externalKey);
167
+ if (!runtime || runtime.closing) {
168
+ return;
169
+ }
170
+ runtime.closing = true;
171
+ if (runtime.idleTimer) {
172
+ clearTimeout(runtime.idleTimer);
173
+ runtime.idleTimer = null;
174
+ }
175
+ await this.options.onClose(externalKey, runtime.acpSessionId);
176
+ this.#runtime.delete(externalKey);
177
+ this.#reverse.delete(runtime.acpSessionId);
178
+ this.#activeKeys.delete(externalKey);
179
+ const waiter = this.#waiters.shift();
180
+ if (waiter) {
181
+ this.#activeKeys.add(waiter.key);
182
+ waiter.resolve();
183
+ }
184
+ }
185
+ /** Rejects queued waiters, closes every active session, and stops granting new slots. */
186
+ async shutdown() {
187
+ this.#shuttingDown = true;
188
+ const pending = this.#waiters.splice(0, this.#waiters.length);
189
+ for (const waiter of pending) {
190
+ waiter.resolve();
191
+ }
192
+ await Promise.all([...this.#runtime.keys()].map((key) => this.#closeRuntime(key)));
193
+ }
194
+ get isShuttingDown() {
195
+ return this.#shuttingDown;
196
+ }
197
+ }
198
+ //# sourceMappingURL=session-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-registry.js","sourceRoot":"","sources":["../../src/daemon/session-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Persisted external-conversation-key → ACP-session-ID binding, so daemon
3
+ * restarts can `session/resume` prior conversations instead of losing them.
4
+ * Append-only JSONL patch log (same fold/tombstone shape as
5
+ * `src/acp/sessions/store.ts`), scoped to the current project.
6
+ */
7
+ export type DaemonSessionBinding = {
8
+ externalKey: string;
9
+ acpSessionId: string;
10
+ cwd: string;
11
+ userId?: string | null;
12
+ createdAt: string;
13
+ updatedAt: string;
14
+ };
15
+ /** Reads and folds the append-only bindings log; unparseable (torn) lines are skipped. */
16
+ export declare function readDaemonSessionBindings(): Promise<Map<string, DaemonSessionBinding>>;
17
+ /** Appends a full or partial binding patch; `updatedAt` is bumped unless the patch sets it. */
18
+ export declare function patchDaemonSessionBinding(patch: Partial<Omit<DaemonSessionBinding, "externalKey">> & {
19
+ externalKey: string;
20
+ }): Promise<void>;
@@ -0,0 +1,63 @@
1
+ import { appendFile, mkdir, readFile } from "node:fs/promises";
2
+ import { dirname, join } from "node:path";
3
+ import { sessionsPath } from "../core/utils/paths.js";
4
+ const bindingsPath = () => join(sessionsPath(), "daemon", "bindings.jsonl");
5
+ function isCompleteBinding(entry) {
6
+ return (typeof entry.externalKey === "string" &&
7
+ typeof entry.acpSessionId === "string" &&
8
+ typeof entry.cwd === "string" &&
9
+ typeof entry.createdAt === "string" &&
10
+ typeof entry.updatedAt === "string");
11
+ }
12
+ /** Reads and folds the append-only bindings log; unparseable (torn) lines are skipped. */
13
+ export async function readDaemonSessionBindings() {
14
+ let raw;
15
+ try {
16
+ raw = await readFile(bindingsPath(), "utf8");
17
+ }
18
+ catch {
19
+ return new Map();
20
+ }
21
+ const folded = new Map();
22
+ for (const line of raw.split("\n")) {
23
+ const trimmed = line.trim();
24
+ if (!trimmed) {
25
+ continue;
26
+ }
27
+ let record;
28
+ try {
29
+ record = JSON.parse(trimmed);
30
+ }
31
+ catch {
32
+ continue;
33
+ }
34
+ if (!record || typeof record.externalKey !== "string") {
35
+ continue;
36
+ }
37
+ if (record.deleted) {
38
+ folded.delete(record.externalKey);
39
+ continue;
40
+ }
41
+ folded.set(record.externalKey, {
42
+ ...folded.get(record.externalKey),
43
+ ...record,
44
+ });
45
+ }
46
+ const entries = new Map();
47
+ for (const [key, entry] of folded) {
48
+ if (isCompleteBinding(entry)) {
49
+ entries.set(key, entry);
50
+ }
51
+ }
52
+ return entries;
53
+ }
54
+ /** Appends a full or partial binding patch; `updatedAt` is bumped unless the patch sets it. */
55
+ export async function patchDaemonSessionBinding(patch) {
56
+ const path = bindingsPath();
57
+ await mkdir(dirname(path), { recursive: true });
58
+ await appendFile(path, `${JSON.stringify({
59
+ ...patch,
60
+ updatedAt: patch.updatedAt ?? new Date().toISOString(),
61
+ })}\n`, "utf8");
62
+ }
63
+ //# sourceMappingURL=session-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../src/daemon/session-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAsBtD,MAAM,YAAY,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,QAAQ,EAAE,gBAAgB,CAAC,CAAC;AAE5E,SAAS,iBAAiB,CACxB,KAAoC;IAEpC,OAAO,CACL,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ;QACrC,OAAO,KAAK,CAAC,YAAY,KAAK,QAAQ;QACtC,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ;QAC7B,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ;QACnC,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,CACpC,CAAC;AACJ,CAAC;AAED,0FAA0F;AAC1F,MAAM,CAAC,KAAK,UAAU,yBAAyB;IAG7C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,EAAE,MAAM,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,GAAG,EAAE,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,GAAG,EAAyC,CAAC;IAChE,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,SAAS;QACX,CAAC;QACD,IAAI,MAAqB,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAkB,CAAC;QAChD,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;YACtD,SAAS;QACX,CAAC;QACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;YAClC,SAAS;QACX,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,EAAE;YAC7B,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;YACjC,GAAG,MAAM;SACV,CAAC,CAAC;IACL,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAgC,CAAC;IACxD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;QAClC,IAAI,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,+FAA+F;AAC/F,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,KAEC;IAED,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;IAC5B,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,UAAU,CACd,IAAI,EACJ,GAAG,IAAI,CAAC,SAAS,CAAC;QAChB,GAAG,KAAK;QACR,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACvD,CAAC,IAAI,EACN,MAAM,CACP,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hoomanjs",
3
- "version": "1.58.0",
3
+ "version": "1.59.0",
4
4
  "description": "Full-stack open-source agentic ecosystem — local-first CLI, VS Code, ACP, daemon channels, and Design mode. BYOK, custom inference endpoints, llama.cpp & MLX.",
5
5
  "author": {
6
6
  "name": "Vaibhav Pandey",
@@ -1,18 +0,0 @@
1
- import type { Manager as McpManager } from "../core/mcp/index.js";
2
- import type { AskUserBackend } from "../core/tools/ask-user.js";
3
- /**
4
- * `ask_user` backend for channel-driven daemon jobs: relays the question to
5
- * the originating MCP server over the `hooman/channel/ask` capability (the
6
- * question-flavoured sibling of `hooman/channel/permission`), so the server
7
- * can surface it wherever the human actually is — a Slack thread, a Telegram
8
- * chat, etc. — and post the answer back.
9
- *
10
- * Jobs without a channel origin, and servers that don't advertise the
11
- * capability, report "unavailable" so the model proceeds on its own
12
- * judgement, matching daemon behaviour before this backend existed.
13
- */
14
- export declare function createDaemonAskUserBackend(manager: McpManager, agent: {
15
- appState: {
16
- get(key: string): unknown;
17
- };
18
- }): AskUserBackend;
@@ -1,44 +0,0 @@
1
- import { readChannelOrigin } from "../core/approvals/channel-ask.js";
2
- /**
3
- * `ask_user` backend for channel-driven daemon jobs: relays the question to
4
- * the originating MCP server over the `hooman/channel/ask` capability (the
5
- * question-flavoured sibling of `hooman/channel/permission`), so the server
6
- * can surface it wherever the human actually is — a Slack thread, a Telegram
7
- * chat, etc. — and post the answer back.
8
- *
9
- * Jobs without a channel origin, and servers that don't advertise the
10
- * capability, report "unavailable" so the model proceeds on its own
11
- * judgement, matching daemon behaviour before this backend existed.
12
- */
13
- export function createDaemonAskUserBackend(manager, agent) {
14
- return {
15
- ask: async (request) => {
16
- const origin = readChannelOrigin(agent);
17
- if (!origin?.server) {
18
- return { kind: "unavailable" };
19
- }
20
- const supported = await manager
21
- .supportsChannelAsk(origin.server)
22
- .catch(() => false);
23
- if (!supported) {
24
- return { kind: "unavailable" };
25
- }
26
- try {
27
- return await manager.requestChannelAsk(origin.server, {
28
- requestId: crypto.randomUUID(),
29
- question: request.question,
30
- options: request.options,
31
- source: origin.source,
32
- user: origin.user,
33
- session: origin.session,
34
- thread: origin.thread,
35
- }, { signal: request.signal });
36
- }
37
- catch {
38
- // Relay failed or timed out; let the model proceed on its own.
39
- return { kind: "unavailable" };
40
- }
41
- },
42
- };
43
- }
44
- //# sourceMappingURL=questions.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"questions.js","sourceRoot":"","sources":["../../src/daemon/questions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAC;AAIrE;;;;;;;;;;GAUG;AACH,MAAM,UAAU,0BAA0B,CACxC,OAAmB,EACnB,KAAkD;IAElD,OAAO;QACL,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACrB,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;YACxC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;gBACpB,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;YACjC,CAAC;YACD,MAAM,SAAS,GAAG,MAAM,OAAO;iBAC5B,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC;iBACjC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;YACtB,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;YACjC,CAAC;YACD,IAAI,CAAC;gBACH,OAAO,MAAM,OAAO,CAAC,iBAAiB,CACpC,MAAM,CAAC,MAAM,EACb;oBACE,SAAS,EAAE,MAAM,CAAC,UAAU,EAAE;oBAC9B,QAAQ,EAAE,OAAO,CAAC,QAAQ;oBAC1B,OAAO,EAAE,OAAO,CAAC,OAAO;oBACxB,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,OAAO,EAAE,MAAM,CAAC,OAAO;oBACvB,MAAM,EAAE,MAAM,CAAC,MAAM;iBACtB,EACD,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAC3B,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,+DAA+D;gBAC/D,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;YACjC,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}