@zaly/agent 0.0.5 → 0.0.6

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.
@@ -1 +1 @@
1
- {"version":3,"file":"session-Bv8JiT8P.mjs","names":[],"sources":["../src/session/memory.ts","../src/session/session.ts"],"sourcesContent":["import type { SessionStore } from \"./store.ts\"\nimport type { SessionNode } from \"./types.ts\"\n\n/**\n * In-memory `SessionStore` — a `Map<uuid, SessionNode>` plus a `root`\n * pointer. No persistence. Construction takes optional initial nodes\n * for tests, replay tools, and the Claude session importer.\n */\nexport class MemoryStore implements SessionStore {\n readonly #nodes: Map<string, SessionNode>\n #root?: SessionNode\n\n constructor(nodes?: Iterable<SessionNode>) {\n this.#nodes = new Map()\n if (nodes) {\n for (const node of nodes) {\n this.#nodes.set(node.uuid, node)\n this.#root = node\n }\n }\n }\n\n get root(): SessionNode | undefined {\n return this.#root\n }\n\n async get(id: string): Promise<SessionNode | undefined> {\n return this.#nodes.get(id)\n }\n\n async write(node: SessionNode): Promise<void> {\n this.#nodes.set(node.uuid, node)\n this.#root = node\n }\n\n *all(): Iterable<SessionNode> {\n yield* this.#nodes.values()\n }\n}\n","import type { Message } from \"@zaly/ai\"\nimport type { SessionStore } from \"./store.ts\"\nimport type {\n MaskCheckpoint,\n PartialNode,\n SessionEvents,\n SessionInit,\n SessionMessage,\n SessionNode,\n SessionNodeView,\n SessionOptions,\n SessionSettings,\n SessionUpdate,\n SessionView,\n} from \"./types.ts\"\n\nimport { Emitter, normPath } from \"@zaly/shared\"\nimport { zalyPaths } from \"@zaly/shared/paths\"\nimport { isDeepStrictEqual } from \"node:util\"\nimport { join } from \"pathe\"\nimport { uuidv7 } from \"../utils/uuid.ts\"\nimport { JsonlStore } from \"./jsonl.ts\"\nimport { MemoryStore } from \"./memory.ts\"\n\nconst VERSION = 2\n\n/**\n * Conversation primitive. Owns a DAG of message + compaction + meta\n * nodes via a `SessionStore` backend; the *active* message list is\n * whatever you get by walking `parentUuid` backwards from the store's\n * root until you hit a `compact` node (or the start).\n *\n * **Meta is snapshot-based.** `session-meta` nodes carry the full\n * cumulative meta as of their commit time. Other node types (start,\n * resume, message, compact) don't carry meta — `Session` reconstructs\n * the cumulative meta at any point by finding the most recent\n * `session-meta` ancestor in a forward pass over the chain. Public\n * APIs return `SessionNodeView` (raw node + decorated `meta`).\n *\n * Designed so the loop, the persistor, the TUI, and any replay tool\n * all read from one source of truth and never mutate the DAG behind\n * each other's backs:\n *\n * - **Run loop** (`Agent`) calls `add()` on each new message and\n * `compact()` on overflow.\n * - **TUI** subscribes to the `node` event for streaming render.\n * - **Persistor** is the `SessionStore` — `MemoryStore` for ephemeral\n * sessions, `JsonlStore` for file-backed.\n *\n * Construction:\n * - Use `await Session.load(opts)` for the recommended path — picks\n * the right store (Memory if no path, JSONL if path).\n * - For tests / direct embedding, `new Session({ store })` works with\n * any pre-built store.\n */\nexport class Session<T extends SessionStore = SessionStore> extends Emitter<SessionEvents> {\n readonly #store: T\n #opts: SessionInit<T>\n #id: string\n #dir: string\n #settings: SessionSettings = {}\n #view: SessionView = { messages: [], nodes: new Map() }\n #closed = false\n #started = false\n #path?: string\n #head?: string\n\n protected constructor(opts: SessionInit<T>) {\n super()\n this.#opts = opts\n this.#path = opts.path\n this.#store = opts.store\n this.#id = opts.defaults?.sessionId ?? uuidv7()\n this.#dir = normPath(opts.dir ?? join(zalyPaths.env.tmp, \"sessions\", this.#id))\n }\n\n /** Recommended one-step path: pick the right store backend, hydrate\n * if applicable, return a ready-to-use session.\n *\n * - `path` provided → `JsonlStore` (file-backed, hydrates from disk\n * if the file exists, opens for append either way).\n * - No `path` → `MemoryStore` (ephemeral, no persistence). */\n static async load<T extends SessionStore = SessionStore>(\n opts: SessionOptions<T> & { store: T }\n ): Promise<Session<T>>\n static async load(opts: SessionOptions & { path: string }): Promise<Session<JsonlStore>>\n static async load(opts?: SessionOptions & {}): Promise<Session<MemoryStore>>\n static async load(opts: SessionOptions = {}): Promise<Session> {\n const session = await Session.create(opts)\n await session.#rebuild()\n return session\n }\n\n private static async create(opts: SessionOptions = {}): Promise<Session> {\n const path = opts.path ? normPath(opts.path) : undefined\n const store: SessionStore =\n opts.store ?? (path ? await JsonlStore.load(path) : new MemoryStore())\n const init: SessionInit = { ...opts, path, store }\n return new Session(init)\n }\n\n /** Efficiently fetch the most recent user message by walking backward until we hit\n * a user message, without reconstructing the full chain. Useful for a session picker. */\n static async lastMessage(opts: SessionOptions = {}): Promise<Message<\"user\"> | undefined> {\n const session = await Session.create(opts)\n let ret: Message<\"user\"> | undefined\n await session.#chain({\n stop: (node) => {\n if (node.type === \"message\" && node.message.role === \"user\") {\n ret = node.message\n return true\n }\n return false\n },\n })\n void session.close()\n return ret\n }\n\n async #rebuild(): Promise<void> {\n const chain = await this.#chain()\n this.#view = chain\n this.#settings = chain.settings\n this.#id = chain.settings.sessionId ?? this.#id\n }\n\n // ── Read ──────────────────────────────────────────────────────────────\n\n /** Active conversation — chain from current head back to the most\n * recent compact (exclusive), in chronological order. This is what\n * the agent sees on the next request. */\n get messages(): readonly Message[] {\n return this.#view.messages\n }\n\n get maskCheckpoint(): MaskCheckpoint | undefined {\n return this.#view.maskCheckpoint\n }\n\n get id(): string {\n return this.#id\n }\n\n get dir(): string {\n return this.#dir\n }\n\n get path(): string | undefined {\n return this.#path\n }\n\n get started(): boolean {\n return this.#started\n }\n\n /** Current session settings, merged with defaults. */\n get settings(): SessionSettings {\n return { ...this.#opts.defaults, ...this.#settings }\n }\n\n async update(settings: SessionUpdate, opts?: { force?: boolean }): Promise<string> {\n return this.#commit({ settings, type: \"session-settings\" }, opts)\n }\n\n /** Lazily emits `session-start` on a fresh session or `session-resume`\n * on a hydrated one. Called from `#commit` before the first non-marker\n * node lands so the start/resume marker always heads the chain. */\n async #autostart(): Promise<void> {\n if (!this.#started) await this.start()\n }\n\n /** Explicitly mark the session as started — writes a `session-start`\n * marker on a fresh session, `session-resume` on a hydrated one.\n * Idempotent; subsequent calls are no-ops. Optional `meta` is\n * applied via `update()` after the marker lands. */\n async start(settings?: SessionUpdate): Promise<string | undefined> {\n if (this.#started) return settings ? this.update(settings) : this.#store.root?.uuid\n this.#started = true\n const type = this.messages.length > 0 ? (\"session-resume\" as const) : (\"session-start\" as const)\n await this.#commit({ type })\n void this.emit(type)\n await this.update(settings ?? {})\n return this.#store.root?.uuid\n }\n\n /** Look up the decorated view for a node id — raw node plus the\n * cumulative meta as of that node's position. Returns the\n * message-typed view, or `undefined` if the id doesn't refer to a\n * message in this session. */\n async node(id?: string | Message): Promise<(SessionNodeView & { type: \"message\" }) | undefined> {\n const m = typeof id === \"string\" ? { id } : id\n if (!m?.id) return undefined\n const ret = this.#view.nodes.get(m.id)\n return ret?.type === \"message\" ? ret : undefined\n }\n\n /** Current head uuid — the root of the store, which is the most\n * recently appended node. */\n get head(): string | undefined {\n return this.#head ?? this.#store.root?.uuid\n }\n\n /** Full DAG iterator — backed by `store.all()`. Yields raw nodes\n * (no meta decoration). */\n nodes(): Iterable<SessionNode> | AsyncIterable<SessionNode> {\n return this.#store.all?.() ?? []\n }\n\n get root(): SessionNode | undefined {\n return this.#store.root\n }\n\n // ── Mutate ────────────────────────────────────────────────────────────\n\n /** Append a message. The new node is parented to the current head,\n * head advances, and a `node` event fires. Optional `meta` carries\n * usage / model / finish info — populate it for assistant turns\n * the agent commits after a step. Returns the assigned uuid.\n *\n * If `message.id` / `message.ts` are set (e.g. round-tripped from a\n * prior load), they're used as the node's uuid / timestamp; otherwise\n * fresh values are generated. The committed message always carries\n * both — `m.id === node.uuid` and `m.ts === node.ts`. */\n async add(message: Message): Promise<string> {\n const uuid = message.id ?? uuidv7()\n const ts = message.ts ?? Date.now()\n const m = { ...message, id: uuid, ts }\n return this.#commit({ message: m, type: \"message\" })\n }\n\n /** Mark a compaction boundary. Subsequent `add()` calls land after\n * this node; their messages form the new active conversation. The\n * pre-compact chain stays in the store but is no longer part of\n * `messages`. Also commits a fresh `session-meta` snapshot right\n * after the compact so post-compact lazy walks always have a nearby\n * meta anchor. */\n async compact(opts: {\n trigger?: \"manual\" | \"auto\"\n preTokens?: number\n durationMs?: number\n tail: number\n summary: Message<\"system\">\n }): Promise<string> {\n const node: SessionNode = {\n durationMs: opts.durationMs,\n parentUuid: this.#store.root?.uuid,\n preTokens: opts.preTokens,\n summary: opts.summary,\n tail: opts.tail,\n trigger: opts.trigger ?? \"manual\",\n ts: Date.now(),\n type: \"compact\",\n uuid: uuidv7(),\n }\n const compactUuid = await this.#commit(node)\n await this.update({}, { force: true })\n await this.#rebuild()\n void this.emit(\"compact\", { node })\n return compactUuid\n }\n\n async addMaskCheckpoint(opts: MaskCheckpoint): Promise<string> {\n this.#view.maskCheckpoint = opts\n return this.#commit({ ...opts, type: \"mask-checkpoint\" })\n }\n\n async checkout(uuid: string): Promise<void> {\n if (this.#closed) throw new Error(\"Session is closed\")\n this.#head = uuid\n await this.#rebuild()\n }\n\n /** Pre-active history of the current chain — messages from before\n * the most recent compact (and earlier compacts), in chronological\n * order. Useful for TUI scrollback that wants to render context the\n * agent itself no longer sees.\n *\n * `limit` truncates from the front (oldest), so the most recent\n * history messages always make it through. */\n async history(limit?: number): Promise<readonly Message[]> {\n const chain = await this.#chain({ active: false, limit })\n return chain.messages\n }\n\n /** Flush + close the underlying store. Subsequent writes throw. */\n async close(): Promise<void> {\n if (this.#closed) return\n this.#closed = true\n await this.#store.close?.()\n }\n\n // ── Internals ────────────────────────────────────────────────────────\n\n /** Atomically commit a node: register it in the store, update active\n * messages, persist, and emit. Single chokepoint for all mutations. */\n async #commit(n: PartialNode, opts: { force?: boolean } = {}): Promise<string> {\n let node: SessionNode = {\n ...n,\n parentUuid: this.head,\n ts: Date.now(),\n uuid: uuidv7(),\n }\n\n if (!this.#started && node.type !== \"session-start\" && node.type !== \"session-resume\") {\n await this.#autostart()\n }\n if (this.#closed) throw new Error(\"Session is closed\")\n\n const next: SessionSettings = {\n ...this.#opts.defaults,\n ...this.#settings,\n ...(node.type === \"session-settings\" ? node.settings : {}),\n sessionId: this.#id,\n version: VERSION,\n }\n if (next.cwd) next.cwd = normPath(next.cwd)\n if (next.workspace) next.workspace = normPath(next.workspace)\n\n if (node.type === \"session-settings\") {\n if (!opts.force && isDeepStrictEqual(this.#settings, next)) return this.#store.root!.uuid\n node = { ...node, settings: next }\n }\n\n node = { ...node, parentUuid: this.head }\n await this.#store.write(node)\n this.#head = undefined // invalidate head cache so it reflects the store's root on next read\n\n // Decorate the new node with cumulative meta and append to the\n // active chain view\n this.#view.nodes.set(node.uuid, { ...node, settings: next })\n if (node.type === \"message\") this.#view.messages.push(node.message)\n\n // cwd / meta event signaling\n if (node.type === \"session-settings\") {\n const prev = this.#settings\n this.#settings = next\n this.#emitChanges(prev)\n }\n\n void this.emit(\"node\", { node })\n return node.uuid\n }\n\n #emitChanges(prev: SessionSettings): void {\n const next = this.#settings\n if (next.cwd && next.cwd !== prev.cwd) void this.emit(\"cwd\", { cwd: next.cwd, prev: prev.cwd })\n if (next.modelId && next.modelId !== prev.modelId)\n void this.emit(\"model\", { model: next.modelId, prev: prev.modelId })\n if (next.reasoning && next.reasoning !== prev.reasoning)\n void this.emit(\"reasoning\", { effort: next.reasoning, prev: prev.reasoning })\n }\n\n /** Walk `parentUuid` from the given node back toward the root,\n * returning the chain as decorated views (each with cumulative meta).\n * The `active` flag controls what counts:\n * - `true` — only the active chain (stops at first compact /\n * session-start, with the kept tail spliced in via\n * the compact's `tail` field, summary substituted\n * in place of the compact node).\n * - `false` — includes everything back to the root\n * Returns chronological order. */\n async #chain(\n opts: {\n active?: boolean\n limit?: number\n uuid?: string\n stop?: (node: SessionNode) => boolean\n } = {}\n ): Promise<SessionView & { settings: SessionSettings }> {\n // Backward pass: collect raw nodes (new to old)\n const reverse: SessionNode[] = []\n let cursor = opts.uuid ?? this.head\n let limit = opts.limit ?? Infinity\n let compact: SessionNode<\"compact\"> | undefined\n const messageNodes: SessionNode<\"message\">[] = []\n let maskCheckpoint: SessionNode<\"mask-checkpoint\"> | undefined\n\n // Walk backward from the head until we hit a compact or the start, collecting nodes.\n while (cursor) {\n // eslint-disable-next-line no-await-in-loop\n const node = await this.#store.get(cursor)\n if (!node || (node.type === \"message\" && messageNodes.length + 1 > limit)) break\n cursor = node.parentUuid\n reverse.push(node)\n // Keep track if the last mask-checkpoint, until we hit a compaction.\n if (node.type === \"mask-checkpoint\" && !compact && !maskCheckpoint) maskCheckpoint = node\n if (node.type === \"message\") messageNodes.push(node)\n if (node.type === \"compact\" && (opts.active ?? true)) {\n if (compact) break // stop at the first compact\n compact = node\n // update limit to the compact's tail\n limit = Math.min(limit, messageNodes.length + node.tail)\n }\n if (opts.stop?.(node)) break\n }\n\n // Forward pass: decorate with cumulative meta. session-meta nodes\n // redefine the meta; everything else inherits the running snapshot.\n const nodes = new Map<string, SessionNodeView>()\n let settings: SessionSettings = {}\n for (const n of reverse.toReversed()) {\n if (n.type === \"session-settings\") settings = n.settings\n nodes.set(n.uuid, { ...n, settings })\n }\n\n this.#migrate(nodes)\n\n const messages: SessionMessage[] = []\n for (const n of messageNodes) {\n // add `id`, `ts` and `modelId` (if assistant turn) to the message\n const m = { ...n.message, id: n.uuid, ts: n.ts }\n if (m.role === \"assistant\") {\n // Add missing modelId to assistant messages if not present\n const s = nodes.get(n.uuid)?.settings ?? {}\n m.meta = { ...m.meta, modelId: m.meta?.modelId ?? s.modelId }\n }\n n.message = m\n messages.push(m)\n }\n\n // Add compaction summary as the first message\n if (compact) {\n messages.push({ ...compact.summary, id: compact.uuid, ts: compact.ts })\n nodes.set(compact.uuid, {\n message: compact.summary,\n parentUuid: compact.parentUuid,\n settings: nodes.get(compact.uuid)?.settings ?? {},\n ts: compact.ts,\n type: \"message\",\n uuid: compact.uuid,\n })\n }\n\n return {\n compact,\n maskCheckpoint,\n messages: messages.toReversed(),\n nodes,\n settings,\n }\n }\n\n #migrate(nodes: Map<string, SessionNodeView>) {\n for (const n of nodes.values()) {\n const version = n.settings.version ?? 0\n if (version === VERSION) continue\n n.settings = { ...n.settings, version: VERSION }\n\n if (n.type === \"compact\") {\n n.summary.meta = { kind: \"compaction-summary\", ...n.summary.meta }\n continue\n }\n\n if (n.type !== \"message\") continue\n const m = n.message\n\n if (\n m.role === \"system\" &&\n Array.isArray(m.content) &&\n m.content[0].type === \"meta\" &&\n m.content[0].tag === \"task\"\n ) {\n // Missing task meta data kind\n m.meta = { kind: \"task\", ...m.meta }\n }\n\n n.message = m\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"session-B1lXSWhT.mjs","names":[],"sources":["../src/session/memory.ts","../src/session/session.ts"],"sourcesContent":["import type { SessionStore } from \"./store.ts\"\nimport type { SessionNode } from \"./types.ts\"\n\n/**\n * In-memory `SessionStore` — a `Map<uuid, SessionNode>` plus a `root`\n * pointer. No persistence. Construction takes optional initial nodes\n * for tests, replay tools, and the Claude session importer.\n */\nexport class MemoryStore implements SessionStore {\n readonly #nodes: Map<string, SessionNode>\n #root?: SessionNode\n\n constructor(nodes?: Iterable<SessionNode>) {\n this.#nodes = new Map()\n if (nodes) {\n for (const node of nodes) {\n this.#nodes.set(node.uuid, node)\n this.#root = node\n }\n }\n }\n\n get root(): SessionNode | undefined {\n return this.#root\n }\n\n async get(id: string): Promise<SessionNode | undefined> {\n return this.#nodes.get(id)\n }\n\n async write(node: SessionNode): Promise<void> {\n this.#nodes.set(node.uuid, node)\n this.#root = node\n }\n\n *all(): Iterable<SessionNode> {\n yield* this.#nodes.values()\n }\n}\n","import type { Message } from \"@zaly/ai\"\nimport type { SessionStore } from \"./store.ts\"\nimport type {\n MaskCheckpoint,\n PartialNode,\n SessionEvents,\n SessionInit,\n SessionMessage,\n SessionNode,\n SessionNodeView,\n SessionOptions,\n SessionSettings,\n SessionUpdate,\n SessionView,\n} from \"./types.ts\"\n\nimport { Emitter, normPath } from \"@zaly/shared\"\nimport { zalyPaths } from \"@zaly/shared/paths\"\nimport { isDeepStrictEqual } from \"node:util\"\nimport { join } from \"pathe\"\nimport { uuidv7 } from \"../utils/uuid.ts\"\nimport { JsonlStore } from \"./jsonl.ts\"\nimport { MemoryStore } from \"./memory.ts\"\n\nconst VERSION = 2\n\n/**\n * Conversation primitive. Owns a DAG of message + compaction + meta\n * nodes via a `SessionStore` backend; the *active* message list is\n * whatever you get by walking `parentUuid` backwards from the store's\n * root until you hit a `compact` node (or the start).\n *\n * **Meta is snapshot-based.** `session-meta` nodes carry the full\n * cumulative meta as of their commit time. Other node types (start,\n * resume, message, compact) don't carry meta — `Session` reconstructs\n * the cumulative meta at any point by finding the most recent\n * `session-meta` ancestor in a forward pass over the chain. Public\n * APIs return `SessionNodeView` (raw node + decorated `meta`).\n *\n * Designed so the loop, the persistor, the TUI, and any replay tool\n * all read from one source of truth and never mutate the DAG behind\n * each other's backs:\n *\n * - **Run loop** (`Agent`) calls `add()` on each new message and\n * `compact()` on overflow.\n * - **TUI** subscribes to the `node` event for streaming render.\n * - **Persistor** is the `SessionStore` — `MemoryStore` for ephemeral\n * sessions, `JsonlStore` for file-backed.\n *\n * Construction:\n * - Use `await Session.load(opts)` for the recommended path — picks\n * the right store (Memory if no path, JSONL if path).\n * - For tests / direct embedding, `new Session({ store })` works with\n * any pre-built store.\n */\nexport class Session<T extends SessionStore = SessionStore> extends Emitter<SessionEvents> {\n readonly #store: T\n #opts: SessionInit<T>\n #id: string\n #dir: string\n #settings: SessionSettings = {}\n #view: SessionView = { messages: [], nodes: new Map() }\n #closed = false\n #started = false\n #path?: string\n #head?: string\n\n protected constructor(opts: SessionInit<T>) {\n super()\n this.#opts = opts\n this.#path = opts.path\n this.#store = opts.store\n this.#id = opts.defaults?.sessionId ?? uuidv7()\n this.#dir = normPath(opts.dir ?? join(zalyPaths.env.tmp, \"sessions\", this.#id))\n }\n\n /** Recommended one-step path: pick the right store backend, hydrate\n * if applicable, return a ready-to-use session.\n *\n * - `path` provided → `JsonlStore` (file-backed, hydrates from disk\n * if the file exists, opens for append either way).\n * - No `path` → `MemoryStore` (ephemeral, no persistence). */\n static async load<T extends SessionStore = SessionStore>(\n opts: SessionOptions<T> & { store: T }\n ): Promise<Session<T>>\n static async load(opts: SessionOptions & { path: string }): Promise<Session<JsonlStore>>\n static async load(opts?: SessionOptions & {}): Promise<Session<MemoryStore>>\n static async load(opts: SessionOptions = {}): Promise<Session> {\n const session = await Session.create(opts)\n await session.#rebuild()\n return session\n }\n\n private static async create(opts: SessionOptions = {}): Promise<Session> {\n const path = opts.path ? normPath(opts.path) : undefined\n const store: SessionStore =\n opts.store ?? (path ? await JsonlStore.load(path) : new MemoryStore())\n const init: SessionInit = { ...opts, path, store }\n return new Session(init)\n }\n\n /** Efficiently fetch the most recent user message by walking backward until we hit\n * a user message, without reconstructing the full chain. Useful for a session picker. */\n static async lastMessage(opts: SessionOptions = {}): Promise<Message<\"user\"> | undefined> {\n const session = await Session.create(opts)\n let ret: Message<\"user\"> | undefined\n await session.#chain({\n stop: (node) => {\n if (node.type === \"message\" && node.message.role === \"user\") {\n ret = node.message\n return true\n }\n return false\n },\n })\n void session.close()\n return ret\n }\n\n async #rebuild(): Promise<void> {\n const chain = await this.#chain()\n this.#view = chain\n this.#settings = chain.settings\n this.#id = chain.settings.sessionId ?? this.#id\n }\n\n // ── Read ──────────────────────────────────────────────────────────────\n\n /** Active conversation — chain from current head back to the most\n * recent compact (exclusive), in chronological order. This is what\n * the agent sees on the next request. */\n get messages(): readonly Message[] {\n return this.#view.messages\n }\n\n get maskCheckpoint(): MaskCheckpoint | undefined {\n return this.#view.maskCheckpoint\n }\n\n get id(): string {\n return this.#id\n }\n\n get dir(): string {\n return this.#dir\n }\n\n get path(): string | undefined {\n return this.#path\n }\n\n get started(): boolean {\n return this.#started\n }\n\n /** Current session settings, merged with defaults. */\n get settings(): SessionSettings {\n return { ...this.#opts.defaults, ...this.#settings }\n }\n\n async update(settings: SessionUpdate, opts?: { force?: boolean }): Promise<string> {\n return this.#commit({ settings, type: \"session-settings\" }, opts)\n }\n\n /** Lazily emits `session-start` on a fresh session or `session-resume`\n * on a hydrated one. Called from `#commit` before the first non-marker\n * node lands so the start/resume marker always heads the chain. */\n async #autostart(): Promise<void> {\n if (!this.#started) await this.start()\n }\n\n /** Explicitly mark the session as started — writes a `session-start`\n * marker on a fresh session, `session-resume` on a hydrated one.\n * Idempotent; subsequent calls are no-ops. Optional `meta` is\n * applied via `update()` after the marker lands. */\n async start(settings?: SessionUpdate): Promise<string | undefined> {\n if (this.#started) return settings ? this.update(settings) : this.#store.root?.uuid\n this.#started = true\n const type = this.messages.length > 0 ? (\"session-resume\" as const) : (\"session-start\" as const)\n await this.#commit({ type })\n void this.emit(type)\n await this.update(settings ?? {})\n return this.#store.root?.uuid\n }\n\n /** Look up the decorated view for a node id — raw node plus the\n * cumulative meta as of that node's position. Returns the\n * message-typed view, or `undefined` if the id doesn't refer to a\n * message in this session. */\n async node(id?: string | Message): Promise<(SessionNodeView & { type: \"message\" }) | undefined> {\n const m = typeof id === \"string\" ? { id } : id\n if (!m?.id) return undefined\n const ret = this.#view.nodes.get(m.id)\n return ret?.type === \"message\" ? ret : undefined\n }\n\n /** Current head uuid — the root of the store, which is the most\n * recently appended node. */\n get head(): string | undefined {\n return this.#head ?? this.#store.root?.uuid\n }\n\n /** Full DAG iterator — backed by `store.all()`. Yields raw nodes\n * (no meta decoration). */\n nodes(): Iterable<SessionNode> | AsyncIterable<SessionNode> {\n return this.#store.all?.() ?? []\n }\n\n get root(): SessionNode | undefined {\n return this.#store.root\n }\n\n // ── Mutate ────────────────────────────────────────────────────────────\n\n /** Append a message. The new node is parented to the current head,\n * head advances, and a `node` event fires. Optional `meta` carries\n * usage / model / finish info — populate it for assistant turns\n * the agent commits after a step. Returns the assigned uuid.\n *\n * If `message.id` / `message.ts` are set (e.g. round-tripped from a\n * prior load), they're used as the node's uuid / timestamp; otherwise\n * fresh values are generated. The committed message always carries\n * both — `m.id === node.uuid` and `m.ts === node.ts`. */\n async add(message: Message): Promise<string> {\n const uuid = message.id ?? uuidv7()\n const ts = message.ts ?? Date.now()\n const m = { ...message, id: uuid, ts }\n return this.#commit({ message: m, type: \"message\" })\n }\n\n /** Mark a compaction boundary. Subsequent `add()` calls land after\n * this node; their messages form the new active conversation. The\n * pre-compact chain stays in the store but is no longer part of\n * `messages`. Also commits a fresh `session-meta` snapshot right\n * after the compact so post-compact lazy walks always have a nearby\n * meta anchor. */\n async compact(opts: {\n trigger?: \"manual\" | \"auto\"\n preTokens?: number\n durationMs?: number\n tail: number\n summary: Message<\"system\">\n }): Promise<string> {\n const node: SessionNode = {\n durationMs: opts.durationMs,\n parentUuid: this.#store.root?.uuid,\n preTokens: opts.preTokens,\n summary: opts.summary,\n tail: opts.tail,\n trigger: opts.trigger ?? \"manual\",\n ts: Date.now(),\n type: \"compact\",\n uuid: uuidv7(),\n }\n const compactUuid = await this.#commit(node)\n await this.update({}, { force: true })\n await this.#rebuild()\n void this.emit(\"compact\", { node })\n return compactUuid\n }\n\n async addMaskCheckpoint(opts: MaskCheckpoint): Promise<string> {\n this.#view.maskCheckpoint = opts\n return this.#commit({ ...opts, type: \"mask-checkpoint\" })\n }\n\n async checkout(uuid: string): Promise<void> {\n if (this.#closed) throw new Error(\"Session is closed\")\n this.#head = uuid\n await this.#rebuild()\n }\n\n /** Pre-active history of the current chain — messages from before\n * the most recent compact (and earlier compacts), in chronological\n * order. Useful for TUI scrollback that wants to render context the\n * agent itself no longer sees.\n *\n * `limit` truncates from the front (oldest), so the most recent\n * history messages always make it through. */\n async history(limit?: number): Promise<readonly Message[]> {\n const chain = await this.#chain({ active: false, limit })\n return chain.messages\n }\n\n /** Flush + close the underlying store. Subsequent writes throw. */\n async close(): Promise<void> {\n if (this.#closed) return\n this.#closed = true\n await this.#store.close?.()\n }\n\n // ── Internals ────────────────────────────────────────────────────────\n\n /** Atomically commit a node: register it in the store, update active\n * messages, persist, and emit. Single chokepoint for all mutations. */\n async #commit(n: PartialNode, opts: { force?: boolean } = {}): Promise<string> {\n let node: SessionNode = {\n ...n,\n parentUuid: this.head,\n ts: Date.now(),\n uuid: uuidv7(),\n }\n\n if (!this.#started && node.type !== \"session-start\" && node.type !== \"session-resume\") {\n await this.#autostart()\n }\n if (this.#closed) throw new Error(\"Session is closed\")\n\n const next: SessionSettings = {\n ...this.#opts.defaults,\n ...this.#settings,\n ...(node.type === \"session-settings\" ? node.settings : {}),\n sessionId: this.#id,\n version: VERSION,\n }\n if (next.cwd) next.cwd = normPath(next.cwd)\n if (next.workspace) next.workspace = normPath(next.workspace)\n\n if (node.type === \"session-settings\") {\n if (!opts.force && isDeepStrictEqual(this.#settings, next)) return this.#store.root!.uuid\n node = { ...node, settings: next }\n }\n\n node = { ...node, parentUuid: this.head }\n await this.#store.write(node)\n this.#head = undefined // invalidate head cache so it reflects the store's root on next read\n\n // Decorate the new node with cumulative meta and append to the\n // active chain view\n this.#view.nodes.set(node.uuid, { ...node, settings: next })\n if (node.type === \"message\") this.#view.messages.push(node.message)\n\n // cwd / meta event signaling\n if (node.type === \"session-settings\") {\n const prev = this.#settings\n this.#settings = next\n this.#emitChanges(prev)\n }\n\n void this.emit(\"node\", { node })\n return node.uuid\n }\n\n #emitChanges(prev: SessionSettings): void {\n const next = this.#settings\n if (next.cwd && next.cwd !== prev.cwd) void this.emit(\"cwd\", { cwd: next.cwd, prev: prev.cwd })\n if (next.modelId && next.modelId !== prev.modelId)\n void this.emit(\"model\", { model: next.modelId, prev: prev.modelId })\n if (next.reasoning && next.reasoning !== prev.reasoning)\n void this.emit(\"reasoning\", { effort: next.reasoning, prev: prev.reasoning })\n }\n\n /** Walk `parentUuid` from the given node back toward the root,\n * returning the chain as decorated views (each with cumulative meta).\n * The `active` flag controls what counts:\n * - `true` — only the active chain (stops at first compact /\n * session-start, with the kept tail spliced in via\n * the compact's `tail` field, summary substituted\n * in place of the compact node).\n * - `false` — includes everything back to the root\n * Returns chronological order. */\n async #chain(\n opts: {\n active?: boolean\n limit?: number\n uuid?: string\n stop?: (node: SessionNode) => boolean\n } = {}\n ): Promise<SessionView & { settings: SessionSettings }> {\n // Backward pass: collect raw nodes (new to old)\n const reverse: SessionNode[] = []\n let cursor = opts.uuid ?? this.head\n let limit = opts.limit ?? Infinity\n let compact: SessionNode<\"compact\"> | undefined\n const messageNodes: SessionNode<\"message\">[] = []\n let maskCheckpoint: SessionNode<\"mask-checkpoint\"> | undefined\n\n // Walk backward from the head until we hit a compact or the start, collecting nodes.\n while (cursor) {\n // eslint-disable-next-line no-await-in-loop\n const node = await this.#store.get(cursor)\n if (!node || (node.type === \"message\" && messageNodes.length + 1 > limit)) break\n cursor = node.parentUuid\n reverse.push(node)\n // Keep track if the last mask-checkpoint, until we hit a compaction.\n if (node.type === \"mask-checkpoint\" && !compact && !maskCheckpoint) maskCheckpoint = node\n if (node.type === \"message\") messageNodes.push(node)\n if (node.type === \"compact\" && (opts.active ?? true)) {\n if (compact) break // stop at the first compact\n compact = node\n // update limit to the compact's tail\n limit = Math.min(limit, messageNodes.length + node.tail)\n }\n if (opts.stop?.(node)) break\n }\n\n // Forward pass: decorate with cumulative meta. session-meta nodes\n // redefine the meta; everything else inherits the running snapshot.\n const nodes = new Map<string, SessionNodeView>()\n let settings: SessionSettings = {}\n for (const n of reverse.toReversed()) {\n if (n.type === \"session-settings\") settings = n.settings\n nodes.set(n.uuid, { ...n, settings })\n }\n\n this.#migrate(nodes)\n\n const messages: SessionMessage[] = []\n for (const n of messageNodes) {\n // add `id`, `ts` and `modelId` (if assistant turn) to the message\n const m = { ...n.message, id: n.uuid, ts: n.ts }\n if (m.role === \"assistant\") {\n // Add missing modelId to assistant messages if not present\n const s = nodes.get(n.uuid)?.settings ?? {}\n m.meta = { ...m.meta, modelId: m.meta?.modelId ?? s.modelId }\n }\n n.message = m\n messages.push(m)\n }\n\n // Add compaction summary as the first message\n if (compact) {\n messages.push({ ...compact.summary, id: compact.uuid, ts: compact.ts })\n nodes.set(compact.uuid, {\n message: compact.summary,\n parentUuid: compact.parentUuid,\n settings: nodes.get(compact.uuid)?.settings ?? {},\n ts: compact.ts,\n type: \"message\",\n uuid: compact.uuid,\n })\n }\n\n return {\n compact,\n maskCheckpoint,\n messages: messages.toReversed(),\n nodes,\n settings,\n }\n }\n\n #migrate(nodes: Map<string, SessionNodeView>) {\n for (const n of nodes.values()) {\n const version = n.settings.version ?? 0\n if (version === VERSION) continue\n n.settings = { ...n.settings, version: VERSION }\n\n if (n.type === \"compact\") {\n n.summary.meta = { kind: \"compaction-summary\", ...n.summary.meta }\n continue\n }\n\n if (n.type !== \"message\") continue\n const m = n.message\n\n if (\n m.role === \"system\" &&\n Array.isArray(m.content) &&\n m.content[0].type === \"meta\" &&\n m.content[0].tag === \"task\"\n ) {\n // Missing task meta data kind\n m.meta = { kind: \"task\", ...m.meta }\n }\n\n n.message = m\n }\n }\n}\n"],"mappings":"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"}
package/dist/session.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { n as isUuidv7Like, r as uuidv7 } from "./uuid-CAdJg4l8.mjs";
2
2
  import { n as JsonlStore, t as JsonlReader } from "./jsonl-CvgEOakL.mjs";
3
- import { r as MemoryStore, t as Session } from "./session-Bv8JiT8P.mjs";
3
+ import { r as MemoryStore, t as Session } from "./session-B1lXSWhT.mjs";
4
4
  import { decodePath, encodePath, normPath, safeStatAsync } from "@zaly/shared";
5
5
  import { basename, dirname, join, relative } from "pathe";
6
6
  import { glob } from "@zaly/shared/glob";
@@ -22,7 +22,7 @@ const subagentTool = defineTool({
22
22
  const id = uuidv7();
23
23
  const sessionPath = join(tmpdir(), `zaly-subagent-${id}.jsonl`);
24
24
  const startedAt = Date.now();
25
- const { Session } = await import("./session-Bv8JiT8P.mjs").then((n) => n.n);
25
+ const { Session } = await import("./session-B1lXSWhT.mjs").then((n) => n.n);
26
26
  const child = await parent.child({
27
27
  prompt: [args.prompt],
28
28
  session: await Session.load({ path: sessionPath })
@@ -133,4 +133,4 @@ const subagentTool = defineTool({
133
133
  //#endregion
134
134
  export { subagentTool };
135
135
 
136
- //# sourceMappingURL=subagent-CX7Zjcws.mjs.map
136
+ //# sourceMappingURL=subagent-Zz-MQHk2.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"subagent-CX7Zjcws.mjs","names":[],"sources":["../src/tools/subagent.ts"],"sourcesContent":["import type { MetaPart, Streamable, TextPart, ToolContext, ToolResult } from \"@zaly/ai\"\n\nimport { AiError, defineTool, stringifyContent } from \"@zaly/ai\"\nimport { tmpdir } from \"node:os\"\nimport { join } from \"pathe\"\nimport { Type } from \"typebox\"\nimport { uuidv7 } from \"../utils/uuid.ts\"\n\n/**\n * Spawn a subagent — a fresh `Agent` instance running on the same model\n * with the same permissions, given a focused task and a curated tool\n * list (the parent's, minus the subagent tool itself when the child\n * would otherwise hit `maxDepth`).\n *\n * Returns a Streamable so the harness's existing grace-window machinery\n * promotes long-running subagents to background tasks: the parent gets a\n * placeholder, the model sees task-list / heartbeat updates, and the\n * final assistant message lands as a `task-done` system inject when the\n * child stops naturally.\n *\n * Persistence: each subagent gets its own JSONL session file under\n * `tmpdir`, keyed by id. The final `<subagent>` MetaPart references the\n * path so the parent can re-load the full conversation if it needs more\n * than the surface answer.\n *\n * Auth: the spawned `Agent` shares the parent's `PermissionManager`\n * instance — same workspaces, same rules. (We don't deep-copy yet; if a\n * subagent legitimately needs a tighter scope, that's a future option.)\n */\n\nexport type SubagentMeta = {\n id: string\n depth: number\n durationMs?: number\n sessionPath: string\n /** Lifecycle state — visible to the model in the `<subagent>` MetaPart\n * so it can tell a partial running snapshot apart from a final\n * answer. `running` snapshots accompany text that's still streaming;\n * `done` snapshots carry the final assistant message. */\n status: \"running\" | \"done\"\n /** Stop reason when the child has finished — `\"natural\"` for a clean\n * finish, anything else surfaces in the parent's `<subagent>` block\n * so the model can see *why* the answer might be incomplete. */\n stop?: string\n /** Aggregate token usage across the child's whole run. */\n usage?: { input: number; output: number }\n}\n\n// oxlint-disable-next-line sort-keys -- semantic field order: name, desc, params, call\nexport const subagentTool = defineTool({\n name: \"subagent\",\n desc:\n \"Spawn a focused subagent with the same model and permissions. Use \" +\n \"for tasks that benefit from a clean context window: deep code \" +\n \"exploration, multi-step research, or anything where dragging the \" +\n \"intermediate steps through your own context would be expensive. The \" +\n \"subagent's final answer comes back as the result; full transcript is \" +\n \"available at the returned `sessionPath` if you need more detail.\",\n parallel: true,\n params: Type.Object({\n description: Type.String({\n description:\n \"Short description of what the subagent should do. Shown to the \" +\n \"user in the TUI. Keep under ~10 words.\",\n }),\n prompt: Type.String({\n description:\n \"System prompt for the subagent — defines its role and constraints. \" +\n \"The subagent does NOT inherit your system prompt; spell out what \" +\n \"it needs to know.\",\n }),\n task: Type.String({\n description:\n \"The actual task the subagent should perform, as if you were sending \" +\n \"it as a user message. Be specific about the deliverable.\",\n }),\n }),\n\n async call(args, ctx: ToolContext<SubagentMeta>): Promise<Streamable> {\n const parent = ctx.agent\n if (!parent) {\n throw new AiError({\n code: \"MISSING_TOOL_CONTEXT\",\n message:\n \"subagent requires an Agent reference on the context (set up by the agent harness).\",\n })\n }\n\n const id = uuidv7()\n const sessionPath = join(tmpdir(), `zaly-subagent-${id}.jsonl`)\n const startedAt = Date.now()\n const { Session } = await import(\"../session/session.ts\")\n\n // `parent.child(...)` handles all the inheritance — cwd, model,\n // permissions, depth + 1, tool list (incl. the loaded skill tool),\n // and the `subagent`-tool filtering at the depth cap.\n const child = await parent.child({\n prompt: [args.prompt],\n session: await Session.load({ path: sessionPath }),\n })\n const depth = child.depth\n\n // Cursor over the child's running text so `poll()` returns only\n // what's new since the last call (mirrors the bash-tool pattern).\n let textBuffer = \"\"\n let cursor = 0\n let finalResult: ToolResult | undefined\n let stopReason: string | undefined\n\n child.on(\"stream-event\", ({ event }) => {\n if (event.type === \"text-delta\" && typeof event.delta === \"string\") {\n textBuffer += event.delta\n }\n })\n child.on(\"stop\", ({ kind }) => {\n stopReason = kind\n })\n\n child.send({ content: args.task, role: \"user\" })\n const runDone = child.run().catch((error: unknown) => {\n stopReason = \"error\"\n throw error\n })\n\n const buildMeta = (running: boolean): SubagentMeta => ({\n depth,\n durationMs: Date.now() - startedAt,\n id,\n sessionPath,\n status: running ? \"running\" : \"done\",\n stop: stopReason,\n usage: { input: child.totalUsage.input, output: child.totalUsage.output },\n })\n\n /** Pull the last assistant text out of the child's session — the\n * user-visible \"answer.\" Falls back to whatever's accumulated in the\n * text buffer if the assistant hasn't committed a message yet. */\n const finalText = (): string => {\n for (let i = child.messages.length - 1; i >= 0; i--) {\n const m = child.messages[i]\n if (m.role !== \"assistant\") continue\n // Assistant content shape excludes Attachment, so it's compatible\n // with `stringifyContent` (which accepts plain Content).\n const text = stringifyContent(m.content as never).trim()\n if (text !== \"\") return text\n }\n return textBuffer.trim()\n }\n\n const buildResult = (running: boolean): ToolResult & { running: boolean } => {\n if (!running && finalResult) return { ...finalResult, running: false }\n\n const meta = buildMeta(running)\n // Live snapshot: emit the meta + only the *new* text since last poll\n // (advancing the cursor), matching the bash tool's increment-poll\n // semantic. The full text is reachable via the session file.\n const slice = textBuffer.slice(cursor)\n cursor = textBuffer.length\n\n // The generic \"still running, more is coming\" notice lives one\n // layer up in `Tasks.#buildPart` (added as a `<task>` MetaPart\n // trailer). Keep this snapshot focused on subagent-specific data\n // — the `<subagent>` envelope + the latest text slice.\n const parts: (MetaPart | TextPart)[] = [{ data: meta, tag: \"subagent\", type: \"meta\" }]\n if (running) {\n if (slice !== \"\") parts.push({ text: slice, type: \"text\" })\n } else {\n const answer = finalText()\n if (answer !== \"\") parts.push({ text: answer, type: \"text\" })\n }\n return { content: parts, isError: false, running }\n }\n\n return {\n abort: () => {\n child.stop()\n },\n done: runDone.then(\n async () => {\n // Flush + close the JSONL writer so the file is fully on disk\n // by the time the parent reads `sessionPath`.\n await child.session.close()\n finalResult = buildResult(false)\n },\n async (error: unknown) => {\n // Surface the error as a subagent result with isError: true.\n // Don't reject the `done` promise — the harness's contract is\n // \"completion is a final snapshot, not a throw.\"\n stopReason ??= \"error\"\n await child.session.close().catch(() => undefined)\n const meta = buildMeta(false)\n const message = error instanceof Error ? error.message : String(error)\n finalResult = {\n content: [\n { data: meta, tag: \"subagent\", type: \"meta\" },\n { text: `subagent failed: ${message}`, type: \"text\" },\n ],\n isError: true,\n }\n }\n ),\n hasNew: () => textBuffer.length > cursor,\n poll: () => buildResult(child.status !== \"idle\" && child.status !== \"paused\"),\n }\n },\n})\n\ndeclare module \"@zaly/ai\" {}\n"],"mappings":"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"}
1
+ {"version":3,"file":"subagent-Zz-MQHk2.mjs","names":[],"sources":["../src/tools/subagent.ts"],"sourcesContent":["import type { MetaPart, Streamable, TextPart, ToolContext, ToolResult } from \"@zaly/ai\"\n\nimport { AiError, defineTool, stringifyContent } from \"@zaly/ai\"\nimport { tmpdir } from \"node:os\"\nimport { join } from \"pathe\"\nimport { Type } from \"typebox\"\nimport { uuidv7 } from \"../utils/uuid.ts\"\n\n/**\n * Spawn a subagent — a fresh `Agent` instance running on the same model\n * with the same permissions, given a focused task and a curated tool\n * list (the parent's, minus the subagent tool itself when the child\n * would otherwise hit `maxDepth`).\n *\n * Returns a Streamable so the harness's existing grace-window machinery\n * promotes long-running subagents to background tasks: the parent gets a\n * placeholder, the model sees task-list / heartbeat updates, and the\n * final assistant message lands as a `task-done` system inject when the\n * child stops naturally.\n *\n * Persistence: each subagent gets its own JSONL session file under\n * `tmpdir`, keyed by id. The final `<subagent>` MetaPart references the\n * path so the parent can re-load the full conversation if it needs more\n * than the surface answer.\n *\n * Auth: the spawned `Agent` shares the parent's `PermissionManager`\n * instance — same workspaces, same rules. (We don't deep-copy yet; if a\n * subagent legitimately needs a tighter scope, that's a future option.)\n */\n\nexport type SubagentMeta = {\n id: string\n depth: number\n durationMs?: number\n sessionPath: string\n /** Lifecycle state — visible to the model in the `<subagent>` MetaPart\n * so it can tell a partial running snapshot apart from a final\n * answer. `running` snapshots accompany text that's still streaming;\n * `done` snapshots carry the final assistant message. */\n status: \"running\" | \"done\"\n /** Stop reason when the child has finished — `\"natural\"` for a clean\n * finish, anything else surfaces in the parent's `<subagent>` block\n * so the model can see *why* the answer might be incomplete. */\n stop?: string\n /** Aggregate token usage across the child's whole run. */\n usage?: { input: number; output: number }\n}\n\n// oxlint-disable-next-line sort-keys -- semantic field order: name, desc, params, call\nexport const subagentTool = defineTool({\n name: \"subagent\",\n desc:\n \"Spawn a focused subagent with the same model and permissions. Use \" +\n \"for tasks that benefit from a clean context window: deep code \" +\n \"exploration, multi-step research, or anything where dragging the \" +\n \"intermediate steps through your own context would be expensive. The \" +\n \"subagent's final answer comes back as the result; full transcript is \" +\n \"available at the returned `sessionPath` if you need more detail.\",\n parallel: true,\n params: Type.Object({\n description: Type.String({\n description:\n \"Short description of what the subagent should do. Shown to the \" +\n \"user in the TUI. Keep under ~10 words.\",\n }),\n prompt: Type.String({\n description:\n \"System prompt for the subagent — defines its role and constraints. \" +\n \"The subagent does NOT inherit your system prompt; spell out what \" +\n \"it needs to know.\",\n }),\n task: Type.String({\n description:\n \"The actual task the subagent should perform, as if you were sending \" +\n \"it as a user message. Be specific about the deliverable.\",\n }),\n }),\n\n async call(args, ctx: ToolContext<SubagentMeta>): Promise<Streamable> {\n const parent = ctx.agent\n if (!parent) {\n throw new AiError({\n code: \"MISSING_TOOL_CONTEXT\",\n message:\n \"subagent requires an Agent reference on the context (set up by the agent harness).\",\n })\n }\n\n const id = uuidv7()\n const sessionPath = join(tmpdir(), `zaly-subagent-${id}.jsonl`)\n const startedAt = Date.now()\n const { Session } = await import(\"../session/session.ts\")\n\n // `parent.child(...)` handles all the inheritance — cwd, model,\n // permissions, depth + 1, tool list (incl. the loaded skill tool),\n // and the `subagent`-tool filtering at the depth cap.\n const child = await parent.child({\n prompt: [args.prompt],\n session: await Session.load({ path: sessionPath }),\n })\n const depth = child.depth\n\n // Cursor over the child's running text so `poll()` returns only\n // what's new since the last call (mirrors the bash-tool pattern).\n let textBuffer = \"\"\n let cursor = 0\n let finalResult: ToolResult | undefined\n let stopReason: string | undefined\n\n child.on(\"stream-event\", ({ event }) => {\n if (event.type === \"text-delta\" && typeof event.delta === \"string\") {\n textBuffer += event.delta\n }\n })\n child.on(\"stop\", ({ kind }) => {\n stopReason = kind\n })\n\n child.send({ content: args.task, role: \"user\" })\n const runDone = child.run().catch((error: unknown) => {\n stopReason = \"error\"\n throw error\n })\n\n const buildMeta = (running: boolean): SubagentMeta => ({\n depth,\n durationMs: Date.now() - startedAt,\n id,\n sessionPath,\n status: running ? \"running\" : \"done\",\n stop: stopReason,\n usage: { input: child.totalUsage.input, output: child.totalUsage.output },\n })\n\n /** Pull the last assistant text out of the child's session — the\n * user-visible \"answer.\" Falls back to whatever's accumulated in the\n * text buffer if the assistant hasn't committed a message yet. */\n const finalText = (): string => {\n for (let i = child.messages.length - 1; i >= 0; i--) {\n const m = child.messages[i]\n if (m.role !== \"assistant\") continue\n // Assistant content shape excludes Attachment, so it's compatible\n // with `stringifyContent` (which accepts plain Content).\n const text = stringifyContent(m.content as never).trim()\n if (text !== \"\") return text\n }\n return textBuffer.trim()\n }\n\n const buildResult = (running: boolean): ToolResult & { running: boolean } => {\n if (!running && finalResult) return { ...finalResult, running: false }\n\n const meta = buildMeta(running)\n // Live snapshot: emit the meta + only the *new* text since last poll\n // (advancing the cursor), matching the bash tool's increment-poll\n // semantic. The full text is reachable via the session file.\n const slice = textBuffer.slice(cursor)\n cursor = textBuffer.length\n\n // The generic \"still running, more is coming\" notice lives one\n // layer up in `Tasks.#buildPart` (added as a `<task>` MetaPart\n // trailer). Keep this snapshot focused on subagent-specific data\n // — the `<subagent>` envelope + the latest text slice.\n const parts: (MetaPart | TextPart)[] = [{ data: meta, tag: \"subagent\", type: \"meta\" }]\n if (running) {\n if (slice !== \"\") parts.push({ text: slice, type: \"text\" })\n } else {\n const answer = finalText()\n if (answer !== \"\") parts.push({ text: answer, type: \"text\" })\n }\n return { content: parts, isError: false, running }\n }\n\n return {\n abort: () => {\n child.stop()\n },\n done: runDone.then(\n async () => {\n // Flush + close the JSONL writer so the file is fully on disk\n // by the time the parent reads `sessionPath`.\n await child.session.close()\n finalResult = buildResult(false)\n },\n async (error: unknown) => {\n // Surface the error as a subagent result with isError: true.\n // Don't reject the `done` promise — the harness's contract is\n // \"completion is a final snapshot, not a throw.\"\n stopReason ??= \"error\"\n await child.session.close().catch(() => undefined)\n const meta = buildMeta(false)\n const message = error instanceof Error ? error.message : String(error)\n finalResult = {\n content: [\n { data: meta, tag: \"subagent\", type: \"meta\" },\n { text: `subagent failed: ${message}`, type: \"text\" },\n ],\n isError: true,\n }\n }\n ),\n hasNew: () => textBuffer.length > cursor,\n poll: () => buildResult(child.status !== \"idle\" && child.status !== \"paused\"),\n }\n },\n})\n\ndeclare module \"@zaly/ai\" {}\n"],"mappings":"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"}
@@ -31,20 +31,29 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
31
  enumerable: true
32
32
  }) : target, mod));
33
33
  //#endregion
34
- //#region ../../node_modules/.bun/shell-quote@1.10.0/node_modules/shell-quote/quote.js
34
+ //#region ../../node_modules/.bun/shell-quote@1.12.0/node_modules/shell-quote/quote.js
35
35
  var require_quote = /* @__PURE__ */ __commonJSMin(((exports, module) => {
36
36
  /** @import { ControlOperator } from './parse' */
37
37
  /** @type {ControlOperator['op'][]} */
38
38
  var OPS = [
39
39
  "||",
40
40
  "&&",
41
+ ";;&",
41
42
  ";;",
43
+ ";&",
42
44
  "|&",
43
45
  "<(",
46
+ ">(",
44
47
  "<<<",
48
+ "<<-",
49
+ "<<",
45
50
  ">>",
46
51
  ">&",
52
+ ">|",
53
+ "&>>",
54
+ "&>",
47
55
  "<&",
56
+ "<>",
48
57
  "&",
49
58
  ";",
50
59
  "(",
@@ -54,15 +63,18 @@ var require_quote = /* @__PURE__ */ __commonJSMin(((exports, module) => {
54
63
  ">"
55
64
  ];
56
65
  var LINE_TERMINATORS = /[\n\r\u2028\u2029]/;
57
- var GLOB_SHELL_SPECIAL = /[\s#!"$&'():;<=>@\\^`|]/g;
66
+ var GLOB_SHELL_SPECIAL = /[\s#!"$&'():;<=>@\\^`|~]/g;
58
67
  /** @type {typeof import('./quote')} */
59
68
  module.exports = function quote(xs) {
69
+ var sawComment = false;
60
70
  return xs.map(function(s) {
71
+ if (sawComment && typeof s === "string" && LINE_TERMINATORS.test(s)) throw new TypeError("a token after a `comment` must not contain line terminators");
61
72
  if (s === "") return "''";
62
73
  if (s && typeof s === "object") {
63
74
  if ("op" in s && s.op === "glob") {
64
75
  if (typeof s.pattern !== "string") throw new TypeError("glob token requires a string `pattern`");
65
76
  if (LINE_TERMINATORS.test(s.pattern)) throw new TypeError("glob `pattern` must not contain line terminators");
77
+ if (s.pattern === "") return "''";
66
78
  return s.pattern.replace(GLOB_SHELL_SPECIAL, "\\$&");
67
79
  }
68
80
  if ("op" in s && typeof s.op === "string") {
@@ -71,18 +83,20 @@ var require_quote = /* @__PURE__ */ __commonJSMin(((exports, module) => {
71
83
  }
72
84
  if ("comment" in s && typeof s.comment === "string") {
73
85
  if (LINE_TERMINATORS.test(s.comment)) throw new TypeError("`comment` must not contain line terminators");
86
+ sawComment = true;
74
87
  return "#" + s.comment;
75
88
  }
76
89
  throw new TypeError("unrecognized object token shape");
77
90
  }
78
- if (/["\s\\]/.test(s) && !/'/.test(s)) return "'" + s.replace(/(['])/g, "\\$1") + "'";
79
- if (/["'\s]/.test(s)) return "\"" + s.replace(/(["\\$`!])/g, "\\$1") + "\"";
91
+ if (/'/.test(s) && /!/.test(s)) return "'" + s.replace(/'/g, "'\"'\"'") + "'";
92
+ if (/["\s\\]/.test(s) && !/'/.test(s)) return "'" + s + "'";
93
+ if (/["'\s]/.test(s)) return "\"" + s.replace(/(["\\$`])/g, "\\$1") + "\"";
80
94
  return String(s).replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}~])/g, "$1\\$2");
81
95
  }).join(" ");
82
96
  };
83
97
  }));
84
98
  //#endregion
85
- //#region ../../node_modules/.bun/shell-quote@1.10.0/node_modules/shell-quote/parse.js
99
+ //#region ../../node_modules/.bun/shell-quote@1.12.0/node_modules/shell-quote/parse.js
86
100
  var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
87
101
  /**
88
102
  * @import {
@@ -93,20 +107,26 @@ var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
93
107
  * } from './parse' */
94
108
  var CONTROL = "(?:" + [
95
109
  "\\|\\|",
96
- "\\&\\&",
97
- ";;",
98
- "\\|\\&",
110
+ "\\&(?:\\&|>>?)",
111
+ ";;\\&?",
112
+ "[;|]\\&",
99
113
  "\\<\\(",
100
114
  "\\<\\<\\<",
115
+ "\\<\\<-",
116
+ "\\<\\<(?!\\()",
101
117
  ">>",
102
- ">\\&",
103
- "<\\&",
118
+ ">[&|(]",
119
+ "<[&>]",
104
120
  "[&;()|<>]"
105
121
  ].join("|") + ")";
106
122
  var controlRE = new RegExp("^" + CONTROL + "$");
107
123
  var META = "|&;()<> \\t";
108
124
  var SINGLE_QUOTE = "'([^']*?)'";
109
- var DOUBLE_QUOTE = "\"((\\\\\"|[^\"])*?)\"";
125
+ var ANSI_C_BODY = "(?:\\\\[\\s\\S]|[^\\\\'])*?";
126
+ var ANSI_C_QUOTE = "\\$'" + ANSI_C_BODY + "'";
127
+ var ansiCAt = new RegExp("\\$'" + ANSI_C_BODY + "(?:(')|\\\\?$)", "g");
128
+ var ANSI_C_LETTERS = "abeEfnrtv";
129
+ var ANSI_C_CHARS = "\x07\b\x1B\x1B\f\n\r \v";
110
130
  var hash = /^#$/;
111
131
  var SQ = "'";
112
132
  var DQ = "\"";
@@ -142,6 +162,38 @@ var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
142
162
  if (typeof r === "object") return pre + TOKEN + JSON.stringify(r) + TOKEN;
143
163
  return pre + r;
144
164
  }
165
+ var ansiCEscape = /\\([0-7]{1,3}|x[\dA-Fa-f]{1,2}|u[\dA-Fa-f]{1,4}|U[\dA-Fa-f]{1,8}|c(?:\\\\|[\s\S])|[abeEfnrtv\\'"?])/g;
166
+ /**
167
+ * @param {string} m
168
+ * @param {string} escape
169
+ */
170
+ function expandAnsiCEscape(m, escape) {
171
+ var kind = escape.charAt(0);
172
+ if (kind === "c") {
173
+ var ctrl = escape.charAt(1);
174
+ return ctrl === "?" ? "" : String.fromCharCode(ctrl.charCodeAt(0) & 31);
175
+ }
176
+ if (kind === "x" || kind === "u" || kind === "U") {
177
+ var cp = parseInt(escape.slice(1), 16);
178
+ if (cp > 1114111) return m;
179
+ return String.fromCharCode.apply(null, cp > 65535 ? [55232 + (cp >> 10), 56320 + (cp & 1023)] : [cp]);
180
+ }
181
+ if (kind >= "0" && kind <= "7") return String.fromCharCode(parseInt(escape, 8) & 255);
182
+ var letter = ANSI_C_LETTERS.indexOf(escape);
183
+ return letter < 0 ? escape : ANSI_C_CHARS.charAt(letter);
184
+ }
185
+ /** @param {string} body */
186
+ function expandAnsiC(body) {
187
+ return body.replace(ansiCEscape, expandAnsiCEscape).split("\0")[0];
188
+ }
189
+ /**
190
+ * @param {string} s
191
+ * @param {number} i
192
+ */
193
+ function closesAnsiC(s, i) {
194
+ ansiCAt.lastIndex = i;
195
+ return !!ansiCAt.exec(s)[1];
196
+ }
145
197
  /**
146
198
  * @param {string} string
147
199
  * @param {Env} [env]
@@ -152,8 +204,9 @@ var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
152
204
  if (!opts) opts = {};
153
205
  var BS = opts.escape || "\\";
154
206
  var ifs = opts.splitUnquoted === true ? " \n" : typeof opts.splitUnquoted === "string" ? opts.splitUnquoted : "";
155
- var BAREWORD = "(\\" + BS + "['\"" + META + "]|[^\\s'\"" + META + "])+";
156
- var matches = matchAll(string, new RegExp(["(" + CONTROL + ")", "(" + BAREWORD + "|" + DOUBLE_QUOTE + "|" + SINGLE_QUOTE + ")+"].join("|"), "g"));
207
+ var BAREWORD = "(\\" + BS + "['\"$\\" + BS + META + "]|\\$\\$|\\$(?!" + ANSI_C_QUOTE.slice(2) + ")|[^\\s'\"$" + META + "])+";
208
+ var DOUBLE_QUOTE = "\"(?:\\" + BS + "[\\s\\S]|[^\"\\" + BS + "])*\"";
209
+ var matches = matchAll(string, new RegExp(["(" + CONTROL + ")", "(" + ANSI_C_QUOTE + "|" + BAREWORD + "|" + DOUBLE_QUOTE + "|" + SINGLE_QUOTE + ")+"].join("|"), "g"));
157
210
  if (matches.length === 0) return [];
158
211
  if (!env) env = {};
159
212
  var commented = false;
@@ -194,10 +247,8 @@ var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
194
247
  varend -= 1;
195
248
  varname = s.slice(i, varend);
196
249
  i = varend;
197
- } else if (/[*@#?$!_-]/.test(char)) {
198
- varname = char;
199
- i += 1;
200
- } else {
250
+ } else if (/[*@#?$!-]/.test(char)) varname = char;
251
+ else {
201
252
  var slicedFromI = s.slice(i);
202
253
  varend = slicedFromI.match(/[^\w\d_]/);
203
254
  if (!varend) {
@@ -251,7 +302,12 @@ var require_parse = /* @__PURE__ */ __commonJSMin(((exports, module) => {
251
302
  if (out.length) return [out, commentObj];
252
303
  return [commentObj];
253
304
  } else if (c === BS) esc = true;
254
- else if (c === DS) {
305
+ else if (c === DS && s.charAt(i + 1) === SQ && closesAnsiC(s, i)) {
306
+ flushRun();
307
+ sawQuote = true;
308
+ out += expandAnsiC(s.slice(i + 2, ansiCAt.lastIndex - 1));
309
+ i = ansiCAt.lastIndex - 1;
310
+ } else if (c === DS) {
255
311
  var value = parseEnvVar();
256
312
  if (!ifs) out += value;
257
313
  else for (var vi = 0; vi < value.length; vi += 1) {
@@ -742,4 +798,4 @@ const TOOLS = {
742
798
  //#endregion
743
799
  export { __toESM as a, __exportAll as i, parseBash as n, __commonJSMin as r, TOOLS as t };
744
800
 
745
- //# sourceMappingURL=tools-Bl-YI890.mjs.map
801
+ //# sourceMappingURL=tools-CZvBFp-v.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools-CZvBFp-v.mjs","names":["shellParse"],"sources":["../../../node_modules/.bun/shell-quote@1.12.0/node_modules/shell-quote/quote.js","../../../node_modules/.bun/shell-quote@1.12.0/node_modules/shell-quote/parse.js","../../../node_modules/.bun/shell-quote@1.12.0/node_modules/shell-quote/index.js","../src/utils/bash/parser.ts","../src/utils/bash/tools.ts"],"sourcesContent":["'use strict';\n\n/** @import { ControlOperator } from './parse' */\n\n/** @type {ControlOperator['op'][]} */\nvar OPS = /** @type {const} */ ([\n\t'||',\n\t'&&',\n\t';;&',\n\t';;',\n\t';&',\n\t'|&',\n\t'<(',\n\t'>(',\n\t'<<<',\n\t'<<-',\n\t'<<',\n\t'>>',\n\t'>&',\n\t'>|',\n\t'&>>',\n\t'&>',\n\t'<&',\n\t'<>',\n\t'&',\n\t';',\n\t'(',\n\t')',\n\t'|',\n\t'<',\n\t'>'\n]);\nvar LINE_TERMINATORS = /[\\n\\r\\u2028\\u2029]/;\nvar GLOB_SHELL_SPECIAL = /[\\s#!\"$&'():;<=>@\\\\^`|~]/g;\n\n/** @type {typeof import('./quote')} */\nmodule.exports = function quote(xs) {\n\tvar sawComment = false;\n\treturn xs.map(function (s) {\n\t\tif (sawComment && typeof s === 'string' && LINE_TERMINATORS.test(s)) {\n\t\t\tthrow new TypeError('a token after a `comment` must not contain line terminators');\n\t\t}\n\t\tif (s === '') {\n\t\t\treturn /** @type {const} */ ('\\'\\'');\n\t\t}\n\t\tif (s && typeof s === 'object') {\n\t\t\tif ('op' in s && s.op === 'glob') {\n\t\t\t\tif (typeof s.pattern !== 'string') {\n\t\t\t\t\tthrow new TypeError('glob token requires a string `pattern`');\n\t\t\t\t}\n\t\t\t\tif (LINE_TERMINATORS.test(s.pattern)) {\n\t\t\t\t\tthrow new TypeError('glob `pattern` must not contain line terminators');\n\t\t\t\t}\n\t\t\t\tif (s.pattern === '') {\n\t\t\t\t\treturn /** @type {const} */ ('\\'\\'');\n\t\t\t\t}\n\t\t\t\treturn s.pattern.replace(GLOB_SHELL_SPECIAL, '\\\\$&');\n\t\t\t}\n\t\t\tif ('op' in s && typeof s.op === 'string') {\n\t\t\t\tif (OPS.indexOf(s.op) < 0) {\n\t\t\t\t\tthrow new TypeError('invalid `op` value: ' + JSON.stringify(s.op));\n\t\t\t\t}\n\t\t\t\treturn s.op.replace(/[\\s\\S]/g, '\\\\$&');\n\t\t\t}\n\t\t\tif ('comment' in s && typeof s.comment === 'string') {\n\t\t\t\tif (LINE_TERMINATORS.test(s.comment)) {\n\t\t\t\t\tthrow new TypeError('`comment` must not contain line terminators');\n\t\t\t\t}\n\t\t\t\tsawComment = true;\n\t\t\t\treturn '#' + s.comment;\n\t\t\t}\n\t\t\tthrow new TypeError('unrecognized object token shape');\n\t\t}\n\t\tif ((/'/).test(s) && (/!/).test(s)) {\n\t\t\treturn \"'\" + s.replace(/'/g, \"'\\\"'\\\"'\") + \"'\";\n\t\t}\n\t\tif ((/[\"\\s\\\\]/).test(s) && !(/'/).test(s)) {\n\t\t\treturn \"'\" + s + \"'\";\n\t\t}\n\t\tif ((/[\"'\\s]/).test(s)) {\n\t\t\treturn '\"' + s.replace(/([\"\\\\$`])/g, '\\\\$1') + '\"';\n\t\t}\n\t\treturn String(s).replace(/([A-Za-z]:)?([#!\"$&'()*,:;<=>?@[\\\\\\]^`{|}~])/g, '$1\\\\$2');\n\t}).join(' ');\n};\n","'use strict';\n\n/**\n * @import {\n * \tControlOperator,\n * \tEnv,\n * \tGlobPattern,\n * \tParseEntry,\n * } from './parse' */\n\n// '<(' and '>(' are process substitution operators and\n// can be parsed the same as control operator\nvar CONTROL = /** @type {const} */ ('(?:') + /** @type {const} */ ([\n\t'\\\\|\\\\|',\n\t'\\\\&(?:\\\\&|>>?)', // `&&`, `&>`, and `&>>`\n\t';;\\\\&?', // `;;` and `;;&`\n\t'[;|]\\\\&', // `;&` and `|&`\n\t'\\\\<\\\\(',\n\t'\\\\<\\\\<\\\\<',\n\t'\\\\<\\\\<-',\n\t'\\\\<\\\\<(?!\\\\()', // `<<(` stays `<` and `<(`, as zsh reads it; other shells reject it\n\t'>>',\n\t'>[&|(]', // `>&`, `>|`, and `>(`\n\t'<[&>]', // `<&` and `<>`\n\t'[&;()|<>]'\n]).join(/** @type {const} */ ('|')) + /** @type {const} */ (')');\nvar controlRE = new RegExp('^' + CONTROL + '$');\nvar META = /** @type {const} */ ('|&;()<> \\\\t');\nvar SINGLE_QUOTE = /** @type {const} */ ('\\'([^\\']*?)\\'');\n// bash ANSI-C quoting, `$'...'`: a backslash escapes the next character, including `'`\nvar ANSI_C_BODY = '(?:\\\\\\\\[\\\\s\\\\S]|[^\\\\\\\\\\'])*?';\nvar ANSI_C_QUOTE = '\\\\$\\'' + ANSI_C_BODY + '\\'';\nvar ansiCAt = new RegExp('\\\\$\\'' + ANSI_C_BODY + '(?:(\\')|\\\\\\\\?$)', 'g');\nvar ANSI_C_LETTERS = 'abeEfnrtv';\nvar ANSI_C_CHARS = '\\x07\\b\\x1B\\x1B\\f\\n\\r\\t\\v';\nvar hash = /^#$/;\n\nvar SQ = /** @type {const} */ (\"'\");\nvar DQ = /** @type {const} */ ('\"');\nvar DS = /** @type {const} */ ('$');\n\nvar TOKEN = '';\nvar mult = /** @type {const} */ (0x100000000); // Math.pow(16, 8);\nfor (var i = 0; i < 4; i++) {\n\tTOKEN += (mult * Math.random()).toString(16);\n}\nvar startsWithToken = new RegExp('^' + TOKEN);\n\n/**\n * @param {string} s\n * @param {RegExp} r\n */\nfunction matchAll(s, r) {\n\tvar origIndex = r.lastIndex;\n\n\tvar matches = [];\n\tvar matchObj;\n\n\twhile ((matchObj = r.exec(s))) {\n\t\tmatches[matches.length] = matchObj;\n\t\tif (r.lastIndex === matchObj.index) {\n\t\t\tr.lastIndex += 1;\n\t\t}\n\t}\n\n\tr.lastIndex = origIndex;\n\n\treturn matches;\n}\n\n/**\n * @param {Env} env\n * @param {string} pre\n * @param {string} key\n */\nfunction getVar(env, pre, key) {\n\tvar r = typeof env === 'function' ? env(key) : env[key];\n\tif (typeof r === 'undefined' && key != '') {\n\t\tr = '';\n\t} else if (typeof r === 'undefined') {\n\t\tr = '$';\n\t}\n\n\tif (typeof r === 'object') {\n\t\treturn pre + TOKEN + JSON.stringify(r) + TOKEN;\n\t}\n\treturn pre + r;\n}\n\nvar ansiCEscape = /\\\\([0-7]{1,3}|x[\\dA-Fa-f]{1,2}|u[\\dA-Fa-f]{1,4}|U[\\dA-Fa-f]{1,8}|c(?:\\\\\\\\|[\\s\\S])|[abeEfnrtv\\\\'\"?])/g;\n\n/**\n * @param {string} m\n * @param {string} escape\n */\nfunction expandAnsiCEscape(m, escape) {\n\tvar kind = escape.charAt(0);\n\tif (kind === 'c') {\n\t\tvar ctrl = escape.charAt(1);\n\t\treturn ctrl === '?' ? '\\x7F' : String.fromCharCode(ctrl.charCodeAt(0) & 0x1F);\n\t}\n\tif (kind === 'x' || kind === 'u' || kind === 'U') {\n\t\tvar cp = parseInt(escape.slice(1), 16);\n\t\tif (cp > 0x10FFFF) {\n\t\t\treturn m;\n\t\t}\n\t\treturn String.fromCharCode.apply(null, cp > 0xFFFF ? [0xD7C0 + (cp >> 10), 0xDC00 + (cp & 0x3FF)] : [cp]);\n\t}\n\tif (kind >= '0' && kind <= '7') {\n\t\treturn String.fromCharCode(parseInt(escape, 8) & 0xFF);\n\t}\n\tvar letter = ANSI_C_LETTERS.indexOf(escape);\n\treturn letter < 0 ? escape : ANSI_C_CHARS.charAt(letter);\n}\n\n/** @param {string} body */\nfunction expandAnsiC(body) {\n\treturn body.replace(ansiCEscape, expandAnsiCEscape).split('\\0')[0]; // like bash, a NUL ends the string\n}\n\n/**\n * @param {string} s\n * @param {number} i\n */\nfunction closesAnsiC(s, i) {\n\tansiCAt.lastIndex = i;\n\treturn !!(/** @type {RegExpExecArray} */ (ansiCAt.exec(s)))[1];\n}\n\n/**\n * @param {string} string\n * @param {Env} [env]\n * @param {{ escape?: string, splitUnquoted?: boolean | string }} [opts]\n * @returns {ParseEntry[]}\n */\nfunction parseInternal(string, env, opts) {\n\tif (!opts) {\n\t\topts = {};\n\t}\n\tvar BS = opts.escape || '\\\\';\n\tvar ifs = opts.splitUnquoted === true ? ' \\t\\n' : (typeof opts.splitUnquoted === 'string' ? opts.splitUnquoted : '');\n\tvar BAREWORD = '(\\\\' + BS + '[\\'\"$\\\\' + BS + META + ']|\\\\$\\\\$|\\\\$(?!' + ANSI_C_QUOTE.slice(2) + ')|[^\\\\s\\'\"$' + META + '])+';\n\tvar DOUBLE_QUOTE = '\"(?:\\\\' + BS + '[\\\\s\\\\S]|[^\"\\\\' + BS + '])*\"';\n\n\tvar chunker = new RegExp([\n\t\t'(' + CONTROL + ')', // control chars\n\t\t'(' + ANSI_C_QUOTE + '|' + BAREWORD + '|' + DOUBLE_QUOTE + '|' + SINGLE_QUOTE + ')+'\n\t].join('|'), 'g');\n\n\tvar matches = matchAll(string, chunker);\n\n\tif (matches.length === 0) {\n\t\treturn [];\n\t}\n\tif (!env) {\n\t\tenv = {};\n\t}\n\n\tvar commented = false;\n\n\treturn matches.map(function (match) {\n\t\tvar s = match[0];\n\t\tif (!s || commented) {\n\t\t\treturn void undefined;\n\t\t}\n\t\tif (controlRE.test(s)) {\n\t\t\treturn /** @type {ControlOperator} */ ({ op: s });\n\t\t}\n\n\t\t// Hand-written scanner/parser for Bash quoting rules:\n\t\t//\n\t\t// 1. inside single quotes, all characters are printed literally.\n\t\t// 2. inside double quotes, all characters are printed literally\n\t\t// except variables prefixed by '$' and backslashes followed by\n\t\t// either a double quote or another backslash.\n\t\t// 3. outside of any quotes, backslashes are treated as escape\n\t\t// characters and not printed (unless they are themselves escaped)\n\t\t// 4. quote context can switch mid-token if there is no whitespace\n\t\t// between the two quote contexts (e.g. all'one'\"token\" parses as\n\t\t// \"allonetoken\")\n\t\t/** @type {string | boolean} */\n\t\tvar quote = false;\n\t\tvar esc = false;\n\t\tvar out = '';\n\t\t/** @type {string[]} */\n\t\tvar words = [];\n\t\tvar sawQuote = false;\n\t\t/** @type {number | null} */\n\t\tvar pendingNw = null;\n\t\tvar isGlob = false;\n\t\t/** @type {number} */\n\t\tvar i;\n\n\t\tfunction parseEnvVar() {\n\t\t\ti += 1;\n\t\t\t/** @type {number | RegExpMatchArray | null} */\n\t\t\tvar varend;\n\t\t\t/** @type {string} */\n\t\t\tvar varname;\n\t\t\tvar char = s.charAt(i);\n\n\t\t\tif (char === '{') {\n\t\t\t\ti += 1;\n\t\t\t\tif (s.charAt(i) === '}') {\n\t\t\t\t\tthrow new Error('Bad substitution: ' + s.slice(i - 2, i + 1));\n\t\t\t\t}\n\t\t\t\t// match braces by depth so a nested `${` keeps its inner `}` from ending the outer substitution\n\t\t\t\tvar depth = 1;\n\t\t\t\tvarend = i;\n\t\t\t\twhile (depth > 0 && varend < s.length) {\n\t\t\t\t\tif (s.charAt(varend) === '{' && s.charAt(varend - 1) === '$') {\n\t\t\t\t\t\tdepth += 1;\n\t\t\t\t\t} else if (s.charAt(varend) === '}') {\n\t\t\t\t\t\tdepth -= 1;\n\t\t\t\t\t}\n\t\t\t\t\tvarend += 1;\n\t\t\t\t}\n\t\t\t\tif (depth !== 0) {\n\t\t\t\t\tthrow new Error('Bad substitution: ' + s.slice(i));\n\t\t\t\t}\n\t\t\t\tvarend -= 1;\n\t\t\t\tvarname = s.slice(i, varend);\n\t\t\t\ti = varend;\n\t\t\t} else if ((/[*@#?$!-]/).test(char)) {\n\t\t\t\tvarname = char;\n\t\t\t} else {\n\t\t\t\tvar slicedFromI = s.slice(i);\n\t\t\t\tvarend = slicedFromI.match(/[^\\w\\d_]/);\n\t\t\t\tif (!varend) {\n\t\t\t\t\tvarname = slicedFromI;\n\t\t\t\t\ti = s.length;\n\t\t\t\t} else {\n\t\t\t\t\tvarname = slicedFromI.slice(0, varend.index);\n\t\t\t\t\ti += /** @type {number} */ (varend.index) - 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn getVar(/** @type {NonNullable<typeof env>} */ (env), '', varname);\n\t\t}\n\n\t\tfunction flushRun() {\n\t\t\tif (pendingNw === null) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (pendingNw === 0) {\n\t\t\t\tif (out !== '') {\n\t\t\t\t\twords[words.length] = out;\n\t\t\t\t\tout = '';\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\twords[words.length] = out;\n\t\t\t\tout = '';\n\t\t\t\tfor (var fe = 1; fe < pendingNw; fe += 1) {\n\t\t\t\t\twords[words.length] = '';\n\t\t\t\t}\n\t\t\t}\n\t\t\tpendingNw = null;\n\t\t}\n\n\t\tfor (i = 0; i < s.length; i++) {\n\t\t\tvar c = s.charAt(i);\n\t\t\tif (ifs && c !== DS) {\n\t\t\t\tflushRun();\n\t\t\t}\n\t\t\tisGlob = isGlob || (!quote && (c === '*' || c === '?'));\n\t\t\tif (esc) {\n\t\t\t\tout += c;\n\t\t\t\tesc = false;\n\t\t\t} else if (quote) {\n\t\t\t\tif (c === quote) {\n\t\t\t\t\tquote = false;\n\t\t\t\t} else if (quote == SQ) {\n\t\t\t\t\tout += c;\n\t\t\t\t} else { // Double quote\n\t\t\t\t\tif (c === BS) {\n\t\t\t\t\t\ti += 1;\n\t\t\t\t\t\tc = s.charAt(i);\n\t\t\t\t\t\tif (c === DQ || c === BS || c === DS) {\n\t\t\t\t\t\t\tout += c;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tout += BS + c;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (c === DS) {\n\t\t\t\t\t\tout += parseEnvVar();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tout += c;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (c === DQ || c === SQ) {\n\t\t\t\tquote = c;\n\t\t\t\tsawQuote = true;\n\t\t\t} else if (controlRE.test(c)) {\n\t\t\t\treturn /** @type {ControlOperator} */ ({ op: s });\n\t\t\t} else if (hash.test(c)) {\n\t\t\t\tcommented = true;\n\t\t\t\tvar commentObj = { comment: string.slice(match.index + i + 1) };\n\t\t\t\tif (out.length) {\n\t\t\t\t\treturn /** @type {const} */ ([out, commentObj]);\n\t\t\t\t}\n\t\t\t\treturn /** @type {const} */ ([commentObj]);\n\t\t\t} else if (c === BS) {\n\t\t\t\tesc = true;\n\t\t\t} else if (c === DS && s.charAt(i + 1) === SQ && closesAnsiC(s, i)) {\n\t\t\t\tflushRun();\n\t\t\t\tsawQuote = true;\n\t\t\t\tout += expandAnsiC(s.slice(i + 2, ansiCAt.lastIndex - 1));\n\t\t\t\ti = ansiCAt.lastIndex - 1;\n\t\t\t} else if (c === DS) {\n\t\t\t\tvar value = parseEnvVar();\n\t\t\t\tif (!ifs) {\n\t\t\t\t\tout += value;\n\t\t\t\t} else {\n\t\t\t\t\tfor (var vi = 0; vi < value.length; vi += 1) {\n\t\t\t\t\t\tvar vc = value.charAt(vi);\n\t\t\t\t\t\tif (ifs.indexOf(vc) < 0) {\n\t\t\t\t\t\t\tflushRun();\n\t\t\t\t\t\t\tout += vc;\n\t\t\t\t\t\t} else if (pendingNw === null) {\n\t\t\t\t\t\t\tpendingNw = vc === ' ' || vc === '\\t' || vc === '\\n' ? 0 : 1;\n\t\t\t\t\t\t} else if (vc !== ' ' && vc !== '\\t' && vc !== '\\n') {\n\t\t\t\t\t\t\tpendingNw += 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tout += c;\n\t\t\t}\n\t\t}\n\n\t\tif (isGlob) {\n\t\t\treturn /** @type {GlobPattern} */ ({ op: 'glob', pattern: out });\n\t\t}\n\n\t\tif (ifs) {\n\t\t\tif (pendingNw !== null && pendingNw > 0) {\n\t\t\t\twords[words.length] = out;\n\t\t\t\tout = '';\n\t\t\t\tfor (var te = 1; te < pendingNw; te += 1) {\n\t\t\t\t\twords[words.length] = '';\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (out !== '' || (sawQuote && words.length === 0)) {\n\t\t\t\twords[words.length] = out;\n\t\t\t}\n\t\t\treturn words;\n\t\t}\n\n\t\treturn out;\n\t}).reduce(function (prev, arg) { // finalize parsed arguments\n\t\tif (typeof arg === 'undefined') {\n\t\t\treturn prev;\n\t\t}\n\t\t/** @type {ParseEntry[]} */ ([]).concat(arg).forEach(function (entry) {\n\t\t\tprev[prev.length] = entry;\n\t\t});\n\t\treturn prev;\n\t}, /** @type {ParseEntry[]} */ ([]));\n}\n\n/** @type {typeof import('./parse')} */\nmodule.exports = function parse(s, env, opts) {\n\tvar mapped = parseInternal(s, env, opts);\n\tif (typeof env !== 'function') {\n\t\treturn mapped;\n\t}\n\treturn mapped.reduce(function (acc, s) {\n\t\tif (typeof s === 'object') {\n\t\t\tacc[acc.length] = s;\n\t\t\treturn acc;\n\t\t}\n\t\tvar xs = s.split(RegExp('(' + TOKEN + '.*?' + TOKEN + ')', 'g'));\n\t\tif (xs.length === 1) {\n\t\t\tacc[acc.length] = xs[0];\n\t\t\treturn acc;\n\t\t}\n\t\txs.filter(Boolean).forEach(function (x) {\n\t\t\tacc[acc.length] = startsWithToken.test(x)\n\t\t\t\t? JSON.parse(x.split(TOKEN)[1])\n\t\t\t\t: x;\n\t\t});\n\t\treturn acc;\n\t}, /** @type {ParseEntry[]} */ ([]));\n};\n","'use strict';\n\nexports.quote = require('./quote');\nexports.parse = require('./parse');\n","import { safeStringify } from \"@zaly/shared\"\nimport { parse as shellParse } from \"shell-quote\"\n\n/** A shell command broken down into its structural parts. One bash\n * input may parse to multiple segments (joined by `|`, `&&`, `||`, `;`).\n * Each segment is evaluated independently by the policy. */\nexport interface Segment {\n cmd: string\n args: string[]\n dyn?: Set<string>\n /** File paths the segment reads from (`< file` redirects). Built-in\n * tools may add inferred reads in the policy layer. */\n reads: string[]\n /** File paths the segment writes to (`> file` / `>> file`). */\n writes: { path: string; mode: \"trunc\" | \"append\" }[]\n /** True if the input contains command substitution (`$()` or backticks)\n * or any other dynamic-evaluation construct. Forces ask. */\n hasCommandSubst: boolean\n}\n\nexport type ParseResult = { ok: true; segments: Segment[] } | { ok: false; reason: string }\n\n/** Targets a redirect can write to without filesystem implications.\n * Anything else is treated as a file write and goes through fileWrite\n * policy. */\nconst SAFE_REDIRECT_TARGETS = new Set([\"/dev/null\", \"/dev/stderr\", \"/dev/stdout\"])\n\n/** A redirect target like `&1`, `&2`, `&-` — duplicates an existing fd,\n * no filesystem effect. */\nfunction isFdRedirect(target: string): boolean {\n return /^&(?:\\d+|-)$/.test(target)\n}\n\ntype Op = { op: string }\ntype Glob = { op: \"glob\"; pattern: string }\ntype Comment = { comment: string }\ntype Token = string | Op | Glob | Comment\n\nconst COMMAND_SEPS = new Set([\"|\", \"&&\", \"||\", \";\"])\nconst REDIRECT_OPS = new Set([\">\", \">>\", \"<\", \">&\", \"<&\"])\nconst DYN_SENTINEL = \"\\u{E000}\"\n\n/** Parse a bash command line. Returns segments with a flag for\n * unsupported constructs that force \"ask\" downstream. Heredocs,\n * subshells, command substitution, and backticks all surface as\n * `hasCommandSubst: true` (the umbrella \"we can't reason about this\"\n * signal) rather than parse failures, so the policy can still decide. */\nexport function parseBash(input: string): ParseResult {\n // Heredocs derail shell-quote (the body bleeds into surrounding tokens),\n // so we bail to ok:false here. checkBash maps that to ask.\n if (/<<-?\\s*['\"]?\\w+/.test(input)) {\n return { ok: false, reason: \"heredoc not supported\" }\n }\n // Backticks command-substitute in bash unless inside single quotes\n // (single quotes preserve everything literally; double quotes don't\n // suppress substitution). Flag only the unquoted form so embedded\n // template literals in `bun -e '... ${x} `tpl` ...'` don't trip us.\n const hasBackticks = hasUnquotedBacktick(input)\n\n let rawTokens: Token[]\n try {\n rawTokens = shellParse(input, (e) =>\n e === \"\" ? undefined : `${DYN_SENTINEL}\\${${e}}`\n ) as Token[]\n } catch (error) {\n return { ok: false, reason: `tokenize: ${(error as Error).message}` }\n }\n\n // Inline plain `(subshell)` contents as separate segments; strip\n // `$(...)` regions and flag as command substitution. Backticks\n // already detected via the pre-scan above.\n const flat = flattenParens(rawTokens)\n const hasCommandSubst = hasBackticks || flat.hasCommandSubst\n\n const segmentTokens: Token[][] = [[]]\n for (const t of flat.tokens) {\n if (typeof t === \"object\" && \"comment\" in t) continue\n\n if (typeof t === \"object\" && \"op\" in t && COMMAND_SEPS.has(t.op)) {\n if (segmentTokens.at(-1)!.length > 0) segmentTokens.push([])\n continue\n }\n\n segmentTokens.at(-1)!.push(t)\n }\n\n if (segmentTokens.at(-1)!.length === 0) segmentTokens.pop()\n\n const segments: Segment[] = []\n for (const segToks of segmentTokens) {\n const seg = parseSegment(segToks)\n if (!seg.ok) return seg\n if (seg.segment) segments.push({ ...seg.segment, hasCommandSubst })\n }\n\n if (segments.length === 0) {\n return { ok: false, reason: \"empty input\" }\n }\n return { ok: true, segments }\n}\n\nfunction parseSegment(\n tokens: Token[]\n):\n | { ok: true; segment: Omit<Segment, \"hasCommandSubst\"> | undefined }\n | { ok: false; reason: string } {\n let args: string[] = []\n let reads: string[] = []\n let writes: { path: string; mode: \"trunc\" | \"append\" }[] = []\n const dyn = new Set<string>()\n\n for (let i = 0; i < tokens.length; i++) {\n const t = tokens[i]\n\n if (typeof t === \"string\") {\n // FD prefix on a redirect (`2>&1`, `2>file`, `1<&-`) — shell-quote\n // splits the leading digit off as its own token. Don't treat it\n // as a regular arg; the redirect handler below picks up the op.\n if (/^\\d+$/.test(t)) {\n const next = tokens[i + 1]\n if (typeof next === \"object\" && \"op\" in next && REDIRECT_OPS.has(next.op)) {\n continue\n }\n }\n args.push(t)\n continue\n }\n\n if (\"comment\" in t) continue\n\n if (\"pattern\" in t) {\n // Pass globs through as their pattern — policy layer decides.\n args.push(t.pattern)\n dyn.add(t.pattern)\n continue\n }\n\n if (!(\"op\" in t)) {\n return { ok: false, reason: `unsupported token: ${safeStringify(t)}` }\n }\n\n if (REDIRECT_OPS.has(t.op)) {\n const next = tokens[i + 1]\n if (typeof next !== \"string\") {\n return { ok: false, reason: `redirect target missing after ${t.op}` }\n }\n i++ // consume target\n\n if (t.op === \">\" || t.op === \">>\") {\n // Skip safe targets (/dev/null etc.) and fd redirects entirely.\n if (SAFE_REDIRECT_TARGETS.has(next) || isFdRedirect(next)) continue\n writes.push({ mode: t.op === \">>\" ? \"append\" : \"trunc\", path: next })\n } else if (t.op === \"<\") {\n if (SAFE_REDIRECT_TARGETS.has(next) || isFdRedirect(next)) continue\n reads.push(next)\n }\n // `>&` / `<&` are fd-duplicating (`2>&1`-style) — no fs impact.\n continue\n }\n\n return { ok: false, reason: `unsupported op: ${t.op}` }\n }\n\n const dynPath = (p: string) => {\n if (!p.includes(DYN_SENTINEL)) return p\n p = p.replaceAll(DYN_SENTINEL, \"\")\n dyn.add(p)\n return p\n }\n\n args = args.map((a) => dynPath(a))\n writes = writes.map((w) => ({ ...w, path: dynPath(w.path) }))\n reads = reads.map(dynPath)\n\n // Strip leading wrapper commands (`time`, `nohup`, `nice`, …) so the\n // policy sees the real command being run. `time bun test` evaluates\n // as `bun test`; a bare `time` (or `time (subshell)` after flatten)\n // leaves nothing and signals the caller to drop the segment.\n const unwrapped = unwrapWrappers(args)\n if (unwrapped.length === 0) {\n return { ok: true, segment: undefined }\n }\n\n return {\n ok: true,\n segment: {\n args: unwrapped.slice(1),\n cmd: unwrapped[0],\n dyn,\n reads,\n writes,\n },\n }\n}\n\ninterface WrapperSpec {\n /** Flags that consume the next arg as their value. */\n valueFlags?: Set<string>\n}\n\n/** Commands that wrap another command — they're transparent for\n * permission purposes, since the security-relevant action is whatever\n * they run. `sudo` is intentionally absent: it changes the effective\n * user and must always be flagged. */\nconst WRAPPERS: Partial<Record<string, WrapperSpec>> = {\n command: {}, // bash builtin, bypasses aliases\n ionice: { valueFlags: new Set([\"-c\", \"-n\", \"-p\", \"-P\", \"-u\"]) },\n nice: { valueFlags: new Set([\"-n\", \"--adjustment\"]) },\n nohup: {},\n time: {}, // bash reserved word, also GNU /usr/bin/time\n}\n\n/** Detect backticks outside single-quoted regions. Single quotes in\n * bash can't be escaped from within, so the toggle is a simple state\n * flip on every `'`. Double quotes do not suppress backtick\n * substitution and are ignored here. */\nfunction hasUnquotedBacktick(input: string): boolean {\n let inSingle = false\n for (const c of input) {\n if (c === \"'\") {\n inSingle = !inSingle\n continue\n }\n if (c === \"`\" && !inSingle) return true\n }\n return false\n}\n\n/** Strip leading wrapper-command prefixes from an arg list. Repeats\n * while the head is a wrapper (so `time nice -n 5 cmd` → `cmd`).\n * Wrapper-flags (and their values, where applicable) are consumed\n * along with the wrapper itself. */\nfunction unwrapWrappers(args: string[]): string[] {\n let current = args\n while (current.length > 0) {\n const spec = WRAPPERS[current[0]]\n if (!spec) break\n const valueFlags = spec.valueFlags ?? new Set()\n let i = 1\n while (i < current.length && current[i].startsWith(\"-\")) {\n const flag = current[i]\n i++\n if (valueFlags.has(flag) && i < current.length) i++ // consume flag value\n }\n current = current.slice(i)\n }\n return current\n}\n\n/** Walk a token list and resolve paren constructs:\n * - Plain `(subshell)` → tokens inlined with `;` boundaries on each\n * side, so the contents become independent segments evaluated by\n * the same policy. The subshell's own scope (cwd changes etc.) is\n * irrelevant to permissions — each command is checked individually.\n * - `$(...)` (command substitution) → stripped entirely, with the\n * `hasCommandSubst` flag raised. The dynamic value can't be\n * statically reasoned about. Detected by the leading `$` token\n * that shell-quote emits before the `(`.\n *\n * Recurses into subshell contents to handle nesting and to catch\n * `$(...)` constructs nested inside subshells. */\nfunction flattenParens(tokens: Token[]): { tokens: Token[]; hasCommandSubst: boolean } {\n const out: Token[] = []\n let hasCommandSubst = false\n let i = 0\n while (i < tokens.length) {\n const t = tokens[i]\n if (typeof t === \"object\" && \"op\" in t && t.op === \"(\") {\n const isSubst = out.at(-1) === \"$\"\n if (isSubst) out.pop()\n\n // Find matching `)`, accounting for nesting.\n let depth = 1\n let j = i + 1\n while (j < tokens.length && depth > 0) {\n const tj = tokens[j]\n if (typeof tj === \"object\" && \"op\" in tj) {\n if (tj.op === \"(\") depth++\n else if (tj.op === \")\") depth--\n }\n if (depth > 0) j++\n }\n const inner = tokens.slice(i + 1, j)\n\n if (isSubst) {\n hasCommandSubst = true\n // Drop the entire $(...) — its value is dynamic.\n } else {\n const sub = flattenParens(inner)\n if (sub.hasCommandSubst) hasCommandSubst = true\n // Surround with separators so subshell contents form their own\n // segments regardless of what's adjacent in the outer chain.\n out.push({ op: \";\" }, ...sub.tokens, { op: \";\" })\n }\n i = j + 1\n continue\n }\n\n if (typeof t === \"object\" && \"op\" in t && t.op === \")\") {\n // Stray closing paren (unbalanced input). Skip — the parser's\n // overall result will likely fail downstream on a malformed segment.\n i++\n continue\n }\n\n out.push(t)\n i++\n }\n return { hasCommandSubst, tokens: out }\n}\n","/**\n * Built-in tool registry — per-command knowledge of which args are\n * input files, output files, and which invocation modes are too\n * complex to safely model.\n *\n * The policy engine consults this registry alongside explicit redirect\n * info from the parser to determine per-command file I/O for the\n * fileRead/fileWrite policy hooks.\n */\nexport interface ToolSpec {\n /** File paths the command will read from, derived from args. */\n reads?: (args: string[]) => string[]\n /** File paths the command will write to. */\n writes?: (args: string[]) => string[]\n /** Returns true if the args invoke a mode the parser can't safely\n * reason about (e.g. `sed -e 'w out'`, `find -exec`). Forces ask. */\n unsafe?: (args: string[]) => boolean\n}\n\n/** All bare positional args (no leading dash). Default for many\n * read-only utilities. */\nfunction positionals(args: string[]): string[] {\n return args.filter((a) => !a.startsWith(\"-\") && a !== \"\")\n}\n\n/** All positionals after the first one (e.g. `grep PATTERN file...`). */\nfunction positionalsAfterFirst(args: string[]): string[] {\n const out: string[] = []\n let seenFirst = false\n for (const a of args) {\n if (a.startsWith(\"-\") || a === \"\") continue\n if (!seenFirst) {\n seenFirst = true\n continue\n }\n out.push(a)\n }\n return out\n}\n\n// ── sed ──────────────────────────────────────────────────────────────────\n\n/** Flags whose value is the *next* arg, not part of the script. */\nconst SED_VALUE_FLAGS = new Set([\"-e\", \"--expression\", \"-f\", \"--file\"])\n\n/** Walk sed args, returning [scripts, positionalsAfterScript]. The\n * \"script\" is everything passed via `-e` plus the first bare arg\n * (the implicit script when no `-e` is given). Positionals after\n * that are input files. */\nfunction sedSplit(args: string[]): { scripts: string[]; files: string[] } {\n const scripts: string[] = []\n const files: string[] = []\n let needsImplicitScript = !args.some(\n (a) => a === \"-e\" || a === \"--expression\" || a === \"-f\" || a === \"--file\"\n )\n\n for (let i = 0; i < args.length; i++) {\n const a = args[i]\n if (a.startsWith(\"-\")) {\n if (SED_VALUE_FLAGS.has(a) && i + 1 < args.length) {\n if (a === \"-e\" || a === \"--expression\") scripts.push(args[i + 1])\n i++ // consume value\n }\n continue\n }\n if (needsImplicitScript) {\n scripts.push(a)\n needsImplicitScript = false\n continue\n }\n files.push(a)\n }\n return { files, scripts }\n}\n\nconst SED_INPLACE = new Set([\"-i\", \"--in-place\"])\n\nfunction sedReads(args: string[]): string[] {\n return sedSplit(args).files\n}\n\nfunction sedWrites(args: string[]): string[] {\n // `-i` rewrites positional input files; also supports `-i.bak` form.\n const inPlace = args.some((a) => SED_INPLACE.has(a) || a.startsWith(\"-i.\"))\n return inPlace ? sedSplit(args).files : []\n}\n\nfunction sedUnsafe(args: string[]): boolean {\n // `w` / `W` write commands and `r` / `R` read commands inside the\n // script can do file I/O the parser can't see. Bail to ask.\n for (const script of sedSplit(args).scripts) {\n if (/(?:^|;|\\n)\\s*[wWrR]\\s+\\S/.test(script)) return true\n }\n return false\n}\n\n// ── awk ──────────────────────────────────────────────────────────────────\n\nfunction awkReads(args: string[]): string[] {\n // awk usage: `awk [opts] 'script' [files...]` or `awk [opts] -f file [files...]`\n // Files come after the script. Skip flags + their values.\n let sawScript = false\n let usingFFlag = false\n const files: string[] = []\n for (let i = 0; i < args.length; i++) {\n const a = args[i]\n if (a === \"-f\" || a === \"--file\") {\n if (i + 1 < args.length) files.push(args[i + 1]) // script file is read too\n i++\n sawScript = true\n usingFFlag = true\n continue\n }\n if (a.startsWith(\"-\")) {\n // Some awk flags take a value (-v, -F)\n if (a === \"-v\" || a === \"-F\") i++\n continue\n }\n if (!sawScript) {\n sawScript = true\n continue\n }\n files.push(a)\n }\n return usingFFlag ? files : files\n}\n\nfunction awkUnsafe(args: string[]): boolean {\n // awk's `print > file` and `printf > file` inside a script write to\n // files. Detecting reliably without parsing the script is hard, so\n // bail to ask if the script (or any -e value) contains a `>` operator.\n // Conservative — false positives are fine for \"ask\".\n let sawScript = false\n for (let i = 0; i < args.length; i++) {\n const a = args[i]\n if (a === \"-f\" || a === \"--file\" || a === \"-v\" || a === \"-F\") {\n i++\n continue\n }\n if (a.startsWith(\"-\")) continue\n if (!sawScript) {\n sawScript = true\n if (/[>|]/.test(a)) return true\n }\n }\n return false\n}\n\n// ── find ─────────────────────────────────────────────────────────────────\n\nfunction findReads(args: string[]): string[] {\n // First positional (or `.`) is the path. Multiple paths possible\n // before the first `-action` flag.\n const out: string[] = []\n for (const a of args) {\n if (a.startsWith(\"-\")) break\n out.push(a)\n }\n return out.length > 0 ? out : [\".\"]\n}\n\nfunction findUnsafe(args: string[]): boolean {\n // `-delete`, `-exec`, `-execdir`, `-ok`, `-okdir` all do work we\n // can't trace.\n return args.some(\n (a) => a === \"-delete\" || a === \"-exec\" || a === \"-execdir\" || a === \"-ok\" || a === \"-okdir\"\n )\n}\n\n// ── dynamic execution ────────────────────────────────────────────────────\n//\n// These commands run arbitrary code passed as arguments or sourced from\n// files. We can't reason about their effects from the parser; force ask.\n\nconst dynamicExec = (): boolean => true\n\n// ── registry ─────────────────────────────────────────────────────────────\n\nexport const TOOLS: Partial<Record<string, ToolSpec>> = {\n \".\": { unsafe: dynamicExec }, // sh: source alias\n awk: { reads: awkReads, unsafe: awkUnsafe },\n basename: {},\n cat: { reads: positionals },\n cd: {},\n cut: { reads: positionals },\n diff: { reads: positionals },\n dirname: {},\n echo: {},\n eval: { unsafe: dynamicExec }, // runs its argument as shell input\n exec: { unsafe: dynamicExec }, // replaces the shell process\n file: { reads: positionals },\n find: { reads: findReads, unsafe: findUnsafe },\n grep: { reads: positionalsAfterFirst },\n head: { reads: positionals },\n hexdump: { reads: positionals },\n less: { reads: positionals },\n ls: { reads: positionals },\n more: { reads: positionals },\n od: { reads: positionals },\n printf: {},\n pwd: {},\n readlink: { reads: positionals },\n realpath: { reads: positionals },\n rg: { reads: positionalsAfterFirst },\n sed: { reads: sedReads, unsafe: sedUnsafe, writes: sedWrites },\n sort: { reads: positionals },\n source: { unsafe: dynamicExec }, // bash: runs the file in current shell\n stat: { reads: positionals },\n tail: { reads: positionals },\n tree: { reads: positionals },\n uniq: { reads: positionals },\n wc: { reads: positionals },\n xxd: { reads: positionals },\n}\n"],"ignoreList":[0,1,2],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zaly/agent",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "description": "Agent runtime, sessions, tools, permissions, compaction, and tasks for zaly.",
5
5
  "keywords": [
6
6
  "agent",
@@ -33,27 +33,27 @@
33
33
  "access": "public"
34
34
  },
35
35
  "dependencies": {
36
- "pathe": "^2.0.3",
37
- "@zaly/ai": "0.0.6",
38
- "@zaly/shared": "0.0.5"
36
+ "@zaly/ai": "0.0.7",
37
+ "@zaly/shared": "0.0.6",
38
+ "pathe": "^2.0.3"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/shell-quote": "^1.7.5",
42
- "ignore": "^7.0.8",
43
- "jsonpath-plus": "^10.4.0",
44
- "shell-quote": "^1.10.0"
42
+ "ignore": "^7.0.12",
43
+ "jsonpath-plus": "^11.2.0",
44
+ "shell-quote": "^1.12.0"
45
45
  },
46
46
  "peerDependencies": {
47
- "typebox": "^1.3.29"
47
+ "typebox": "^1.3.36"
48
48
  },
49
49
  "engines": {
50
50
  "bun": ">=1.2",
51
- "node": ">=22.11"
51
+ "node": ">=22.18.0"
52
52
  },
53
53
  "inlinedDependencies": {
54
- "ignore": "7.0.8",
55
- "jsonpath-plus": "10.4.0",
56
- "shell-quote": "1.10.0"
54
+ "ignore": "7.0.12",
55
+ "jsonpath-plus": "11.2.0",
56
+ "shell-quote": "1.12.0"
57
57
  },
58
58
  "scripts": {
59
59
  "build": "z build",