@agentconnect.md/daemon 1.0.0-rc.214 → 1.0.0-rc.216

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,6 +1,6 @@
1
1
  import { a as agentChildEnv, i as selectAgent, o as loadConfig } from "./load-agents-nWFFmN5-.js";
2
2
  import { l as resolveRoot } from "./paths-DEcUO8lC.js";
3
- import { _ as resolveRuntimes, i as prepareWorkspace, t as AcpHost } from "./acp-host-C1MZcqZo.js";
3
+ import { _ as resolveRuntimes, i as prepareWorkspace, t as AcpHost } from "./acp-host-D4Gc3jWq.js";
4
4
  import { createInterface } from "node:readline";
5
5
  //#region src/cli/chat.ts
6
6
  function renderUpdate(out) {
@@ -75,4 +75,4 @@ async function runChat(opts) {
75
75
  //#endregion
76
76
  export { renderUpdate, runChat };
77
77
 
78
- //# sourceMappingURL=chat-BjhxExGt.js.map
78
+ //# sourceMappingURL=chat-IAmHAZsX.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-BjhxExGt.js","names":[],"sources":["../src/cli/chat.ts"],"sourcesContent":["import { createInterface } from 'node:readline'\nimport type { ContentBlock, SessionUpdate } from '@agentclientprotocol/sdk'\nimport { loadConfig } from '../config/load-config.js'\nimport { resolveRuntimes } from '../runtimes/registry.js'\nimport { selectAgent } from '../agents/load-agents.js'\nimport { agentChildEnv } from '../agents/agent-env.js'\nimport { prepareWorkspace } from '../workspace/workspace-manager.js'\nimport { AcpHost } from '../acp/acp-host.js'\nimport { resolveRoot } from '../paths.js'\n\nexport function renderUpdate(out: NodeJS.WritableStream) {\n return (_sessionId: string, update: SessionUpdate): void => {\n if (update.sessionUpdate === 'agent_message_chunk' && update.content.type === 'text') {\n out.write(update.content.text)\n } else if (update.sessionUpdate === 'tool_call') {\n const title = ('title' in update && update.title) || update.toolCallId || 'tool'\n out.write(`\\n[tool] ${title}\\n`)\n }\n }\n}\n\nexport interface RunChatOpts {\n agentsDir?: string\n agentName?: string\n message?: string\n configPath?: string\n root?: string\n out?: NodeJS.WritableStream\n input?: NodeJS.ReadableStream\n}\n\nexport async function runChat(opts: RunChatOpts): Promise<void> {\n const out = opts.out ?? process.stdout\n const root = resolveRoot(opts.root)\n const cfg = loadConfig({\n root: opts.root,\n configPath: opts.configPath,\n optional: true,\n overrides: { agentsDir: opts.agentsDir }\n })\n const agent = selectAgent(cfg.agentsDir!, opts.agentName)\n\n const runtimes = await resolveRuntimes(cfg, root, { neededRuntimes: [agent.runtime], mode: 'cache-first' })\n const runtime = runtimes[agent.runtime]\n if (!runtime) {\n const available = Object.keys(runtimes).sort().join(', ') || '(none)'\n throw new Error(`runtime \"${agent.runtime}\" not found. Available: ${available}`)\n }\n\n const host = new AcpHost(runtime, {\n onUpdate: renderUpdate(out),\n env: agentChildEnv(agent),\n configPrefs: {\n model: agent.runtimeOverrides?.model,\n reasoningEffort: agent.reasoningEffort,\n fastMode: agent.fastMode\n }\n })\n await host.start()\n try {\n const cwd = await prepareWorkspace(agent)\n const sessionId = await host.newSession(cwd)\n\n const send = async (text: string) => {\n const blocks: ContentBlock[] = [{ type: 'text', text }]\n await host.prompt(sessionId, blocks)\n out.write('\\n')\n }\n\n if (typeof opts.message === 'string' && opts.message.length > 0) {\n await send(opts.message)\n return\n }\n\n const rl = createInterface({ input: opts.input ?? process.stdin })\n out.write(`Chatting with ${agent.name} (${agent.id}). Type .exit to quit.\\n> `)\n try {\n for await (const line of rl) {\n const text = line.trim()\n if (text === '.exit' || text === '.quit') break\n if (text.length > 0) await send(text)\n out.write('> ')\n }\n } finally {\n rl.close()\n }\n } finally {\n await host.stop()\n }\n}\n"],"mappings":";;;;;AAUA,SAAgB,aAAa,KAA4B;CACvD,QAAQ,YAAoB,WAAgC;EAC1D,IAAI,OAAO,kBAAkB,yBAAyB,OAAO,QAAQ,SAAS,QAC5E,IAAI,MAAM,OAAO,QAAQ,IAAI;OACxB,IAAI,OAAO,kBAAkB,aAAa;GAC/C,MAAM,QAAS,WAAW,UAAU,OAAO,SAAU,OAAO,cAAc;GAC1E,IAAI,MAAM,YAAY,MAAM,GAAG;EACjC;CACF;AACF;AAYA,eAAsB,QAAQ,MAAkC;CAC9D,MAAM,MAAM,KAAK,OAAO,QAAQ;CAChC,MAAM,OAAO,YAAY,KAAK,IAAI;CAClC,MAAM,MAAM,WAAW;EACrB,MAAM,KAAK;EACX,YAAY,KAAK;EACjB,UAAU;EACV,WAAW,EAAE,WAAW,KAAK,UAAU;CACzC,CAAC;CACD,MAAM,QAAQ,YAAY,IAAI,WAAY,KAAK,SAAS;CAExD,MAAM,WAAW,MAAM,gBAAgB,KAAK,MAAM;EAAE,gBAAgB,CAAC,MAAM,OAAO;EAAG,MAAM;CAAc,CAAC;CAC1G,MAAM,UAAU,SAAS,MAAM;CAC/B,IAAI,CAAC,SAAS;EACZ,MAAM,YAAY,OAAO,KAAK,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,KAAK;EAC7D,MAAM,IAAI,MAAM,YAAY,MAAM,QAAQ,0BAA0B,WAAW;CACjF;CAEA,MAAM,OAAO,IAAI,QAAQ,SAAS;EAChC,UAAU,aAAa,GAAG;EAC1B,KAAK,cAAc,KAAK;EACxB,aAAa;GACX,OAAO,MAAM,kBAAkB;GAC/B,iBAAiB,MAAM;GACvB,UAAU,MAAM;EAClB;CACF,CAAC;CACD,MAAM,KAAK,MAAM;CACjB,IAAI;EACF,MAAM,MAAM,MAAM,iBAAiB,KAAK;EACxC,MAAM,YAAY,MAAM,KAAK,WAAW,GAAG;EAE3C,MAAM,OAAO,OAAO,SAAiB;GACnC,MAAM,SAAyB,CAAC;IAAE,MAAM;IAAQ;GAAK,CAAC;GACtD,MAAM,KAAK,OAAO,WAAW,MAAM;GACnC,IAAI,MAAM,IAAI;EAChB;EAEA,IAAI,OAAO,KAAK,YAAY,YAAY,KAAK,QAAQ,SAAS,GAAG;GAC/D,MAAM,KAAK,KAAK,OAAO;GACvB;EACF;EAEA,MAAM,KAAK,gBAAgB,EAAE,OAAO,KAAK,SAAS,QAAQ,MAAM,CAAC;EACjE,IAAI,MAAM,iBAAiB,MAAM,KAAK,IAAI,MAAM,GAAG,2BAA2B;EAC9E,IAAI;GACF,WAAW,MAAM,QAAQ,IAAI;IAC3B,MAAM,OAAO,KAAK,KAAK;IACvB,IAAI,SAAS,WAAW,SAAS,SAAS;IAC1C,IAAI,KAAK,SAAS,GAAG,MAAM,KAAK,IAAI;IACpC,IAAI,MAAM,IAAI;GAChB;EACF,UAAU;GACR,GAAG,MAAM;EACX;CACF,UAAU;EACR,MAAM,KAAK,KAAK;CAClB;AACF"}
1
+ {"version":3,"file":"chat-IAmHAZsX.js","names":[],"sources":["../src/cli/chat.ts"],"sourcesContent":["import { createInterface } from 'node:readline'\nimport type { ContentBlock, SessionUpdate } from '@agentclientprotocol/sdk'\nimport { loadConfig } from '../config/load-config.js'\nimport { resolveRuntimes } from '../runtimes/registry.js'\nimport { selectAgent } from '../agents/load-agents.js'\nimport { agentChildEnv } from '../agents/agent-env.js'\nimport { prepareWorkspace } from '../workspace/workspace-manager.js'\nimport { AcpHost } from '../acp/acp-host.js'\nimport { resolveRoot } from '../paths.js'\n\nexport function renderUpdate(out: NodeJS.WritableStream) {\n return (_sessionId: string, update: SessionUpdate): void => {\n if (update.sessionUpdate === 'agent_message_chunk' && update.content.type === 'text') {\n out.write(update.content.text)\n } else if (update.sessionUpdate === 'tool_call') {\n const title = ('title' in update && update.title) || update.toolCallId || 'tool'\n out.write(`\\n[tool] ${title}\\n`)\n }\n }\n}\n\nexport interface RunChatOpts {\n agentsDir?: string\n agentName?: string\n message?: string\n configPath?: string\n root?: string\n out?: NodeJS.WritableStream\n input?: NodeJS.ReadableStream\n}\n\nexport async function runChat(opts: RunChatOpts): Promise<void> {\n const out = opts.out ?? process.stdout\n const root = resolveRoot(opts.root)\n const cfg = loadConfig({\n root: opts.root,\n configPath: opts.configPath,\n optional: true,\n overrides: { agentsDir: opts.agentsDir }\n })\n const agent = selectAgent(cfg.agentsDir!, opts.agentName)\n\n const runtimes = await resolveRuntimes(cfg, root, { neededRuntimes: [agent.runtime], mode: 'cache-first' })\n const runtime = runtimes[agent.runtime]\n if (!runtime) {\n const available = Object.keys(runtimes).sort().join(', ') || '(none)'\n throw new Error(`runtime \"${agent.runtime}\" not found. Available: ${available}`)\n }\n\n const host = new AcpHost(runtime, {\n onUpdate: renderUpdate(out),\n env: agentChildEnv(agent),\n configPrefs: {\n model: agent.runtimeOverrides?.model,\n reasoningEffort: agent.reasoningEffort,\n fastMode: agent.fastMode\n }\n })\n await host.start()\n try {\n const cwd = await prepareWorkspace(agent)\n const sessionId = await host.newSession(cwd)\n\n const send = async (text: string) => {\n const blocks: ContentBlock[] = [{ type: 'text', text }]\n await host.prompt(sessionId, blocks)\n out.write('\\n')\n }\n\n if (typeof opts.message === 'string' && opts.message.length > 0) {\n await send(opts.message)\n return\n }\n\n const rl = createInterface({ input: opts.input ?? process.stdin })\n out.write(`Chatting with ${agent.name} (${agent.id}). Type .exit to quit.\\n> `)\n try {\n for await (const line of rl) {\n const text = line.trim()\n if (text === '.exit' || text === '.quit') break\n if (text.length > 0) await send(text)\n out.write('> ')\n }\n } finally {\n rl.close()\n }\n } finally {\n await host.stop()\n }\n}\n"],"mappings":";;;;;AAUA,SAAgB,aAAa,KAA4B;CACvD,QAAQ,YAAoB,WAAgC;EAC1D,IAAI,OAAO,kBAAkB,yBAAyB,OAAO,QAAQ,SAAS,QAC5E,IAAI,MAAM,OAAO,QAAQ,IAAI;OACxB,IAAI,OAAO,kBAAkB,aAAa;GAC/C,MAAM,QAAS,WAAW,UAAU,OAAO,SAAU,OAAO,cAAc;GAC1E,IAAI,MAAM,YAAY,MAAM,GAAG;EACjC;CACF;AACF;AAYA,eAAsB,QAAQ,MAAkC;CAC9D,MAAM,MAAM,KAAK,OAAO,QAAQ;CAChC,MAAM,OAAO,YAAY,KAAK,IAAI;CAClC,MAAM,MAAM,WAAW;EACrB,MAAM,KAAK;EACX,YAAY,KAAK;EACjB,UAAU;EACV,WAAW,EAAE,WAAW,KAAK,UAAU;CACzC,CAAC;CACD,MAAM,QAAQ,YAAY,IAAI,WAAY,KAAK,SAAS;CAExD,MAAM,WAAW,MAAM,gBAAgB,KAAK,MAAM;EAAE,gBAAgB,CAAC,MAAM,OAAO;EAAG,MAAM;CAAc,CAAC;CAC1G,MAAM,UAAU,SAAS,MAAM;CAC/B,IAAI,CAAC,SAAS;EACZ,MAAM,YAAY,OAAO,KAAK,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,KAAK;EAC7D,MAAM,IAAI,MAAM,YAAY,MAAM,QAAQ,0BAA0B,WAAW;CACjF;CAEA,MAAM,OAAO,IAAI,QAAQ,SAAS;EAChC,UAAU,aAAa,GAAG;EAC1B,KAAK,cAAc,KAAK;EACxB,aAAa;GACX,OAAO,MAAM,kBAAkB;GAC/B,iBAAiB,MAAM;GACvB,UAAU,MAAM;EAClB;CACF,CAAC;CACD,MAAM,KAAK,MAAM;CACjB,IAAI;EACF,MAAM,MAAM,MAAM,iBAAiB,KAAK;EACxC,MAAM,YAAY,MAAM,KAAK,WAAW,GAAG;EAE3C,MAAM,OAAO,OAAO,SAAiB;GACnC,MAAM,SAAyB,CAAC;IAAE,MAAM;IAAQ;GAAK,CAAC;GACtD,MAAM,KAAK,OAAO,WAAW,MAAM;GACnC,IAAI,MAAM,IAAI;EAChB;EAEA,IAAI,OAAO,KAAK,YAAY,YAAY,KAAK,QAAQ,SAAS,GAAG;GAC/D,MAAM,KAAK,KAAK,OAAO;GACvB;EACF;EAEA,MAAM,KAAK,gBAAgB,EAAE,OAAO,KAAK,SAAS,QAAQ,MAAM,CAAC;EACjE,IAAI,MAAM,iBAAiB,MAAM,KAAK,IAAI,MAAM,GAAG,2BAA2B;EAC9E,IAAI;GACF,WAAW,MAAM,QAAQ,IAAI;IAC3B,MAAM,OAAO,KAAK,KAAK;IACvB,IAAI,SAAS,WAAW,SAAS,SAAS;IAC1C,IAAI,KAAK,SAAS,GAAG,MAAM,KAAK,IAAI;IACpC,IAAI,MAAM,IAAI;GAChB;EACF,UAAU;GACR,GAAG,MAAM;EACX;CACF,UAAU;EACR,MAAM,KAAK,KAAK;CAClB;AACF"}
package/dist/index.js CHANGED
@@ -68501,9 +68501,9 @@ const telemetry = startDaemonOpenTelemetry({ serviceVersion: DAEMON_VERSION });
68501
68501
  const [{ Command }, { fileURLToPath: fileURLToPath$1 }, { runChat }, { runLogin }, { runForeground }, { resolveController }, { acquireSingletonLock }, { resolveRoot }] = await Promise.all([
68502
68502
  import("./commander-B5QVEFzH.js"),
68503
68503
  import("node:url"),
68504
- import("./chat-BjhxExGt.js"),
68505
- import("./login-CmjeJfAo.js"),
68506
- import("./run-foreground-CdY92tUb.js"),
68504
+ import("./chat-IAmHAZsX.js"),
68505
+ import("./login-CfZuYjo1.js"),
68506
+ import("./run-foreground-CXY4t5pU.js"),
68507
68507
  import("./service-6w0WKhdx.js"),
68508
68508
  import("./lock-CA9Bm8_s.js"),
68509
68509
  import("./paths-CcRUjFjy.js")
@@ -1,8 +1,8 @@
1
1
  import { t as DAEMON_VERSION } from "./version-DGaRXjBK.js";
2
2
  import { c as ConfigSchema } from "./load-agents-nWFFmN5-.js";
3
3
  import { l as resolveRoot, t as configPath } from "./paths-DEcUO8lC.js";
4
- import { f as buildEnvelope, l as isFrame, m as encode, p as decodeEnvelope } from "./acp-host-C1MZcqZo.js";
5
- import { n as ClientTransport, t as runForeground } from "./run-foreground-CD2uFFvp.js";
4
+ import { f as buildEnvelope, l as isFrame, m as encode, p as decodeEnvelope } from "./acp-host-D4Gc3jWq.js";
5
+ import { n as ClientTransport, t as runForeground } from "./run-foreground-BroK115h.js";
6
6
  import { n as resolveController } from "./service-BQVw7wbz.js";
7
7
  import { dirname } from "node:path";
8
8
  import { fileURLToPath } from "node:url";
@@ -226,4 +226,4 @@ async function runLogin(opts, partial = {}) {
226
226
  //#endregion
227
227
  export { buildInstallOpts, persistCredentials, runLogin };
228
228
 
229
- //# sourceMappingURL=login-CmjeJfAo.js.map
229
+ //# sourceMappingURL=login-CfZuYjo1.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"login-CmjeJfAo.js","names":["realRunForeground"],"sources":["../src/cp/auth-probe.ts","../src/cli/login.ts"],"sourcesContent":["/**\n * `probeAuth` — a one-shot CP auth check used by `agentconnect login`. It dials\n * the CP, sends ONLY the `auth` frame (no `register`), and resolves with the\n * authoritative daemonId from `auth/ok`, or a human-readable failure reason on\n * a `4401` close, a correlated `error`, a dial failure, or a timeout. It always\n * closes the socket and never leaves a connection open.\n */\nimport { buildEnvelope, decodeEnvelope, encode, isFrame } from '@agentconnect.md/protocol'\nimport { ClientTransport, type Transport } from './transport.js'\n\nexport interface ProbeResult {\n ok: boolean\n daemonId?: string\n reason?: string\n}\n\nexport interface ProbeOpts {\n url: string\n token: string\n daemonId?: string\n agentVersion?: string\n timeoutMs?: number\n /** Injectable for tests; defaults to dialing the real WebSocket. */\n connect?: () => Promise<Transport>\n}\n\nexport async function probeAuth(opts: ProbeOpts): Promise<ProbeResult> {\n const timeoutMs = opts.timeoutMs ?? 10_000\n const dial = opts.connect ?? (() => ClientTransport.dial(opts.url))\n\n let transport: Transport\n try {\n transport = await dial()\n } catch (err) {\n return { ok: false, reason: `cannot reach ${opts.url}: ${(err as Error).message}` }\n }\n\n const authPayload: Record<string, unknown> = {\n apiKey: opts.token,\n agentVersion: opts.agentVersion ?? 'probe'\n }\n if (opts.daemonId) authPayload.daemonId = opts.daemonId\n const frame = buildEnvelope('auth', authPayload)\n\n return await new Promise<ProbeResult>((resolve) => {\n let settled = false\n const done = (r: ProbeResult): void => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n try {\n transport.close(1000, 'probe done')\n } catch {\n // socket may already be closed\n }\n resolve(r)\n }\n\n const timer = setTimeout(\n () => done({ ok: false, reason: `timed out after ${timeoutMs}ms waiting for auth/ok` }),\n timeoutMs\n )\n\n transport.onMessage((text) => {\n const decoded = decodeEnvelope(text)\n if (!decoded.ok) return\n const f = decoded.frame\n if (f.corr !== frame.id) return\n if (f.type === 'error') {\n const e = f.payload as { code: string; message: string }\n done({ ok: false, reason: `${e.code}: ${e.message}` })\n return\n }\n if (isFrame('auth/ok')(f)) {\n const ok = f.payload as { daemonId: string }\n done({ ok: true, daemonId: ok.daemonId })\n }\n })\n\n transport.onClose((code) => {\n if (code === 4401) done({ ok: false, reason: 'authentication failed (4401) — check the daemon token' })\n else done({ ok: false, reason: `connection closed (${code}) before auth/ok` })\n })\n\n transport.send(encode(frame))\n })\n}\n","/**\n * `agentconnect login` — interactive onboarding. It tests the Control Plane auth\n * flow, persists credentials only on success, then offers to install the OS\n * service. If installed it starts the service (`up`) and returns; otherwise it\n * runs the daemon in the foreground (the same path as `agentconnect run`).\n *\n * `persistCredentials` is the pure config-merge half (token-only onboarding:\n * `daemonId` is omitted unless given — the first connect adopts it from the\n * token's `sub`). `runLogin` is the orchestrator, with injectable deps for tests.\n */\nimport { readFileSync, existsSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { createInterface } from 'node:readline'\nimport { fileURLToPath } from 'node:url'\nimport { ConfigSchema } from '../config/config-schema.js'\nimport { resolveRoot, configPath } from '../paths.js'\nimport { DAEMON_VERSION } from '../version.js'\nimport { probeAuth, type ProbeResult } from '../cp/auth-probe.js'\nimport { resolveController, type InstallOpts } from '../service/index.js'\nimport { runForeground as realRunForeground } from './run-foreground.js'\n\nexport interface PersistCredsOpts {\n cpUrl: string\n cpKey: string\n daemonId?: string\n root?: string\n configPath?: string\n}\n\n/** Merge url/token into config.json (validated), returning the written path. */\nexport function persistCredentials(opts: PersistCredsOpts): string {\n const root = resolveRoot(opts.root)\n const file = opts.configPath ?? configPath(root)\n const raw: Record<string, unknown> = existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : { version: 1 }\n\n const cp = (raw.controlPlane as Record<string, unknown> | undefined) ?? {}\n raw.controlPlane = { ...cp, url: opts.cpUrl, key: opts.cpKey, enabled: true }\n if (opts.daemonId) raw.daemonId = opts.daemonId\n\n ConfigSchema.parse(raw) // fail before writing if the merge is invalid\n\n mkdirSync(dirname(file), { recursive: true })\n writeFileSync(file, JSON.stringify(raw, null, 2) + '\\n')\n return file\n}\n\nexport interface RunLoginOpts {\n cpUrl?: string\n cpKey?: string\n daemonId?: string\n root?: string\n configPath?: string\n /** The resolved CLI entry path. Must be supplied by the CLI entry point\n * (index.ts) so the correct bundle path is used rather than inferring it\n * from login.ts's own import.meta.url (which is the same bundle but the\n * `../` path calculation would be wrong). Falls back to import.meta.url\n * for the pure test path where login.ts is loaded standalone. */\n cliEntry?: string\n}\n\n/**\n * Build the InstallOpts for the OS service installer.\n *\n * Exported as a pure helper so it can be unit-tested independently of the\n * full `runLogin` orchestration. The caller (index.ts) passes `cliEntry` so\n * the correct bundle path always flows through; if omitted we fall back to\n * `import.meta.url` (correct only when login.ts is the entry, e.g. in tests).\n */\nexport function buildInstallOpts(opts: { root?: string; cliEntry?: string }): InstallOpts {\n return {\n execPath: process.execPath,\n cliEntry: opts.cliEntry ?? fileURLToPath(import.meta.url),\n includeRootEnv: resolveRoot(opts.root) !== resolveRoot(undefined)\n }\n}\n\nexport interface LoginDeps {\n probe: (o: { url: string; token: string }) => Promise<ProbeResult>\n installService: () => Promise<void>\n runForeground: () => Promise<void>\n out: NodeJS.WritableStream\n input: NodeJS.ReadableStream\n isTTY: boolean\n}\n\nfunction realDeps(opts: RunLoginOpts): LoginDeps {\n return {\n probe: (o) => probeAuth({ url: o.url, token: o.token, agentVersion: DAEMON_VERSION }),\n installService: async () => {\n const controller = resolveController({ root: opts.root })\n await controller.install(buildInstallOpts(opts))\n await controller.up()\n },\n runForeground: () =>\n realRunForeground({\n root: opts.root,\n overrides: { cpUrl: opts.cpUrl, cpKey: opts.cpKey, daemonId: opts.daemonId }\n }),\n out: process.stdout,\n input: process.stdin,\n isTTY: Boolean(process.stdin.isTTY)\n }\n}\n\n/** Read the next line from a readline interface via async iterator. */\nasync function nextLine(\n iter: AsyncIterableIterator<string>,\n prompt: string,\n out: NodeJS.WritableStream\n): Promise<string> {\n out.write(prompt)\n const { value, done } = await iter.next()\n if (done) throw new Error(`unexpected end of input waiting for: ${prompt}`)\n return (value as string).trim()\n}\n\nexport async function runLogin(opts: RunLoginOpts, partial: Partial<LoginDeps> = {}): Promise<void> {\n const deps: LoginDeps = { ...realDeps(opts), ...partial }\n const { out } = deps\n\n // ── Non-interactive: flags required; probe; persist. No prompts, no handoff. ──\n if (!deps.isTTY) {\n if (!opts.cpUrl) throw new Error('login requires --cp-url <url>')\n if (!opts.cpKey) throw new Error('login requires --cp-key <key>')\n const r = await deps.probe({ url: opts.cpUrl, token: opts.cpKey })\n if (!r.ok) throw new Error(`authentication failed: ${r.reason}`)\n persistCredentials({ ...opts, cpUrl: opts.cpUrl, cpKey: opts.cpKey })\n out.write(`✓ authenticated${r.daemonId ? ` (daemon ${r.daemonId})` : ''} — credentials saved\\n`)\n return\n }\n\n // Both interactive handoffs read `<root>/config.json`: the installed service\n // runs `… run` (no `--config`), and the foreground `Daemon` loads by root. A\n // custom `--config` would persist credentials the daemon never reads, so the\n // install/run branch would look successful but start without them. Reject it;\n // `--root` is the knob that keeps persist + service + foreground in sync.\n if (opts.configPath) {\n throw new Error(\n 'interactive login does not support --config — use --root <dir> so the daemon and the installed service read the same config'\n )\n }\n\n // ── Interactive ──\n const rl = createInterface({ input: deps.input, output: out })\n const iter = rl[Symbol.asyncIterator]()\n let url = opts.cpUrl\n let token = opts.cpKey\n try {\n if (!url) url = await nextLine(iter, 'Control Plane URL: ', out)\n\n for (let attempt = 0; ; attempt++) {\n if (!token) token = await nextLine(iter, 'Daemon API key: ', out)\n out.write(`Testing connection to ${url}…\\n`)\n const r = await deps.probe({ url: url!, token: token! })\n if (r.ok) {\n out.write(`✓ authenticated${r.daemonId ? ` (daemon ${r.daemonId})` : ''}\\n`)\n break\n }\n out.write(`✗ ${r.reason}\\n`)\n if (attempt >= 1) throw new Error('authentication failed')\n token = undefined // re-prompt on retry\n }\n\n persistCredentials({ ...opts, cpUrl: url!, cpKey: token! })\n\n const ans = (await nextLine(iter, 'Install AgentConnect as a background service? (y/N) ', out)).toLowerCase()\n if (ans === 'y' || ans === 'yes') {\n await deps.installService()\n out.write('Service installed and started. Manage it with `agentconnect up` / `down` / `status`.\\n')\n return\n }\n out.write('Starting in the foreground (Ctrl-C to stop)…\\n')\n } finally {\n rl.close()\n }\n\n await deps.runForeground()\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"login-CfZuYjo1.js","names":["realRunForeground"],"sources":["../src/cp/auth-probe.ts","../src/cli/login.ts"],"sourcesContent":["/**\n * `probeAuth` — a one-shot CP auth check used by `agentconnect login`. It dials\n * the CP, sends ONLY the `auth` frame (no `register`), and resolves with the\n * authoritative daemonId from `auth/ok`, or a human-readable failure reason on\n * a `4401` close, a correlated `error`, a dial failure, or a timeout. It always\n * closes the socket and never leaves a connection open.\n */\nimport { buildEnvelope, decodeEnvelope, encode, isFrame } from '@agentconnect.md/protocol'\nimport { ClientTransport, type Transport } from './transport.js'\n\nexport interface ProbeResult {\n ok: boolean\n daemonId?: string\n reason?: string\n}\n\nexport interface ProbeOpts {\n url: string\n token: string\n daemonId?: string\n agentVersion?: string\n timeoutMs?: number\n /** Injectable for tests; defaults to dialing the real WebSocket. */\n connect?: () => Promise<Transport>\n}\n\nexport async function probeAuth(opts: ProbeOpts): Promise<ProbeResult> {\n const timeoutMs = opts.timeoutMs ?? 10_000\n const dial = opts.connect ?? (() => ClientTransport.dial(opts.url))\n\n let transport: Transport\n try {\n transport = await dial()\n } catch (err) {\n return { ok: false, reason: `cannot reach ${opts.url}: ${(err as Error).message}` }\n }\n\n const authPayload: Record<string, unknown> = {\n apiKey: opts.token,\n agentVersion: opts.agentVersion ?? 'probe'\n }\n if (opts.daemonId) authPayload.daemonId = opts.daemonId\n const frame = buildEnvelope('auth', authPayload)\n\n return await new Promise<ProbeResult>((resolve) => {\n let settled = false\n const done = (r: ProbeResult): void => {\n if (settled) return\n settled = true\n clearTimeout(timer)\n try {\n transport.close(1000, 'probe done')\n } catch {\n // socket may already be closed\n }\n resolve(r)\n }\n\n const timer = setTimeout(\n () => done({ ok: false, reason: `timed out after ${timeoutMs}ms waiting for auth/ok` }),\n timeoutMs\n )\n\n transport.onMessage((text) => {\n const decoded = decodeEnvelope(text)\n if (!decoded.ok) return\n const f = decoded.frame\n if (f.corr !== frame.id) return\n if (f.type === 'error') {\n const e = f.payload as { code: string; message: string }\n done({ ok: false, reason: `${e.code}: ${e.message}` })\n return\n }\n if (isFrame('auth/ok')(f)) {\n const ok = f.payload as { daemonId: string }\n done({ ok: true, daemonId: ok.daemonId })\n }\n })\n\n transport.onClose((code) => {\n if (code === 4401) done({ ok: false, reason: 'authentication failed (4401) — check the daemon token' })\n else done({ ok: false, reason: `connection closed (${code}) before auth/ok` })\n })\n\n transport.send(encode(frame))\n })\n}\n","/**\n * `agentconnect login` — interactive onboarding. It tests the Control Plane auth\n * flow, persists credentials only on success, then offers to install the OS\n * service. If installed it starts the service (`up`) and returns; otherwise it\n * runs the daemon in the foreground (the same path as `agentconnect run`).\n *\n * `persistCredentials` is the pure config-merge half (token-only onboarding:\n * `daemonId` is omitted unless given — the first connect adopts it from the\n * token's `sub`). `runLogin` is the orchestrator, with injectable deps for tests.\n */\nimport { readFileSync, existsSync, writeFileSync, mkdirSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { createInterface } from 'node:readline'\nimport { fileURLToPath } from 'node:url'\nimport { ConfigSchema } from '../config/config-schema.js'\nimport { resolveRoot, configPath } from '../paths.js'\nimport { DAEMON_VERSION } from '../version.js'\nimport { probeAuth, type ProbeResult } from '../cp/auth-probe.js'\nimport { resolveController, type InstallOpts } from '../service/index.js'\nimport { runForeground as realRunForeground } from './run-foreground.js'\n\nexport interface PersistCredsOpts {\n cpUrl: string\n cpKey: string\n daemonId?: string\n root?: string\n configPath?: string\n}\n\n/** Merge url/token into config.json (validated), returning the written path. */\nexport function persistCredentials(opts: PersistCredsOpts): string {\n const root = resolveRoot(opts.root)\n const file = opts.configPath ?? configPath(root)\n const raw: Record<string, unknown> = existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : { version: 1 }\n\n const cp = (raw.controlPlane as Record<string, unknown> | undefined) ?? {}\n raw.controlPlane = { ...cp, url: opts.cpUrl, key: opts.cpKey, enabled: true }\n if (opts.daemonId) raw.daemonId = opts.daemonId\n\n ConfigSchema.parse(raw) // fail before writing if the merge is invalid\n\n mkdirSync(dirname(file), { recursive: true })\n writeFileSync(file, JSON.stringify(raw, null, 2) + '\\n')\n return file\n}\n\nexport interface RunLoginOpts {\n cpUrl?: string\n cpKey?: string\n daemonId?: string\n root?: string\n configPath?: string\n /** The resolved CLI entry path. Must be supplied by the CLI entry point\n * (index.ts) so the correct bundle path is used rather than inferring it\n * from login.ts's own import.meta.url (which is the same bundle but the\n * `../` path calculation would be wrong). Falls back to import.meta.url\n * for the pure test path where login.ts is loaded standalone. */\n cliEntry?: string\n}\n\n/**\n * Build the InstallOpts for the OS service installer.\n *\n * Exported as a pure helper so it can be unit-tested independently of the\n * full `runLogin` orchestration. The caller (index.ts) passes `cliEntry` so\n * the correct bundle path always flows through; if omitted we fall back to\n * `import.meta.url` (correct only when login.ts is the entry, e.g. in tests).\n */\nexport function buildInstallOpts(opts: { root?: string; cliEntry?: string }): InstallOpts {\n return {\n execPath: process.execPath,\n cliEntry: opts.cliEntry ?? fileURLToPath(import.meta.url),\n includeRootEnv: resolveRoot(opts.root) !== resolveRoot(undefined)\n }\n}\n\nexport interface LoginDeps {\n probe: (o: { url: string; token: string }) => Promise<ProbeResult>\n installService: () => Promise<void>\n runForeground: () => Promise<void>\n out: NodeJS.WritableStream\n input: NodeJS.ReadableStream\n isTTY: boolean\n}\n\nfunction realDeps(opts: RunLoginOpts): LoginDeps {\n return {\n probe: (o) => probeAuth({ url: o.url, token: o.token, agentVersion: DAEMON_VERSION }),\n installService: async () => {\n const controller = resolveController({ root: opts.root })\n await controller.install(buildInstallOpts(opts))\n await controller.up()\n },\n runForeground: () =>\n realRunForeground({\n root: opts.root,\n overrides: { cpUrl: opts.cpUrl, cpKey: opts.cpKey, daemonId: opts.daemonId }\n }),\n out: process.stdout,\n input: process.stdin,\n isTTY: Boolean(process.stdin.isTTY)\n }\n}\n\n/** Read the next line from a readline interface via async iterator. */\nasync function nextLine(\n iter: AsyncIterableIterator<string>,\n prompt: string,\n out: NodeJS.WritableStream\n): Promise<string> {\n out.write(prompt)\n const { value, done } = await iter.next()\n if (done) throw new Error(`unexpected end of input waiting for: ${prompt}`)\n return (value as string).trim()\n}\n\nexport async function runLogin(opts: RunLoginOpts, partial: Partial<LoginDeps> = {}): Promise<void> {\n const deps: LoginDeps = { ...realDeps(opts), ...partial }\n const { out } = deps\n\n // ── Non-interactive: flags required; probe; persist. No prompts, no handoff. ──\n if (!deps.isTTY) {\n if (!opts.cpUrl) throw new Error('login requires --cp-url <url>')\n if (!opts.cpKey) throw new Error('login requires --cp-key <key>')\n const r = await deps.probe({ url: opts.cpUrl, token: opts.cpKey })\n if (!r.ok) throw new Error(`authentication failed: ${r.reason}`)\n persistCredentials({ ...opts, cpUrl: opts.cpUrl, cpKey: opts.cpKey })\n out.write(`✓ authenticated${r.daemonId ? ` (daemon ${r.daemonId})` : ''} — credentials saved\\n`)\n return\n }\n\n // Both interactive handoffs read `<root>/config.json`: the installed service\n // runs `… run` (no `--config`), and the foreground `Daemon` loads by root. A\n // custom `--config` would persist credentials the daemon never reads, so the\n // install/run branch would look successful but start without them. Reject it;\n // `--root` is the knob that keeps persist + service + foreground in sync.\n if (opts.configPath) {\n throw new Error(\n 'interactive login does not support --config — use --root <dir> so the daemon and the installed service read the same config'\n )\n }\n\n // ── Interactive ──\n const rl = createInterface({ input: deps.input, output: out })\n const iter = rl[Symbol.asyncIterator]()\n let url = opts.cpUrl\n let token = opts.cpKey\n try {\n if (!url) url = await nextLine(iter, 'Control Plane URL: ', out)\n\n for (let attempt = 0; ; attempt++) {\n if (!token) token = await nextLine(iter, 'Daemon API key: ', out)\n out.write(`Testing connection to ${url}…\\n`)\n const r = await deps.probe({ url: url!, token: token! })\n if (r.ok) {\n out.write(`✓ authenticated${r.daemonId ? ` (daemon ${r.daemonId})` : ''}\\n`)\n break\n }\n out.write(`✗ ${r.reason}\\n`)\n if (attempt >= 1) throw new Error('authentication failed')\n token = undefined // re-prompt on retry\n }\n\n persistCredentials({ ...opts, cpUrl: url!, cpKey: token! })\n\n const ans = (await nextLine(iter, 'Install AgentConnect as a background service? (y/N) ', out)).toLowerCase()\n if (ans === 'y' || ans === 'yes') {\n await deps.installService()\n out.write('Service installed and started. Manage it with `agentconnect up` / `down` / `status`.\\n')\n return\n }\n out.write('Starting in the foreground (Ctrl-C to stop)…\\n')\n } finally {\n rl.close()\n }\n\n await deps.runForeground()\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { n as require_ms, t as require_src$2 } from "./src-DDtlVutj.js";
3
3
  import { t as DAEMON_VERSION } from "./version-DGaRXjBK.js";
4
4
  import { a as agentChildEnv, i as selectAgent, n as findAgentFiles, o as loadConfig, r as loadAgents, s as persistDaemonId } from "./load-agents-nWFFmN5-.js";
5
5
  import { l as resolveRoot, o as mcpSocketPath, u as statePath } from "./paths-DEcUO8lC.js";
6
- import { _ as resolveRuntimes, a as initGitInjection, c as simpleGit, d as MAX_FRAME_BYTES, f as buildEnvelope, g as cachedRuntimeVersions, h as cachedRuntimeNames, i as prepareWorkspace, m as encode, n as installedRuntimes, o as probeGitVersion, p as decodeEnvelope, r as prefetchWorkspace, s as sessionGitEnv, t as AcpHost, u as normalizeGitUrl } from "./acp-host-C1MZcqZo.js";
6
+ import { _ as resolveRuntimes, a as initGitInjection, c as simpleGit, d as MAX_FRAME_BYTES, f as buildEnvelope, g as cachedRuntimeVersions, h as cachedRuntimeNames, i as prepareWorkspace, m as encode, n as installedRuntimes, o as probeGitVersion, p as decodeEnvelope, r as prefetchWorkspace, s as sessionGitEnv, t as AcpHost, u as normalizeGitUrl } from "./acp-host-D4Gc3jWq.js";
7
7
  import { i as GitCredentialCache, n as gitcredSocketPath, r as writeGitcredShim, t as GitCredServer } from "./gitcred-server-Dcsq6QJe.js";
8
8
  import { n as encodeFrame, t as decodeFrames } from "./ipc-2Xbxe7uu.js";
9
9
  import * as sp from "node:path";
@@ -5280,20 +5280,23 @@ function watch$1(paths, options = {}) {
5280
5280
  /**
5281
5281
  * Runtime config surfaced to the ACP child as environment variables, under
5282
5282
  * `AGENTCONNECT_*` for runtimes that read them. ACP `session/new`/`initialize`
5283
- * carry no model / effort / system-prompt field (SDK v1); model + reasoning
5284
- * effort are additionally applied per session via ACP session config options
5283
+ * carry no model / effort field (SDK v1); model + reasoning effort are
5284
+ * additionally applied per session via ACP session config options
5285
5285
  * (`session/set_config_option`) when the runtime advertises the selectors —
5286
5286
  * see AcpHost.applySessionConfig — which is how claude-acp actually honors
5287
- * them. The env remains the only channel for the system prompt and for
5288
- * runtimes without config options. (`runtimeOverrides.env` is applied
5289
- * separately by `agentChildEnv`.) Only emits a key when the value is set.
5287
+ * them. The env stays the fallback for runtimes without config options.
5288
+ *
5289
+ * The system prompt is NOT here: it rides `_meta.systemPrompt` on
5290
+ * session/new|load for Claude runtimes (see claudeSessionMeta), and for other
5291
+ * runtimes it's inlined as the first prompt text block (see AcpHost.inlineSystemPrompt
5292
+ * + SessionManager). (`runtimeOverrides.env` is applied separately by
5293
+ * `agentChildEnv`.) Only emits a key when the value is set.
5290
5294
  */
5291
5295
  function cpRuntimeEnv(agent) {
5292
5296
  const out = {};
5293
5297
  if (agent.runtimeOverrides?.model) out.AGENTCONNECT_MODEL = agent.runtimeOverrides.model;
5294
5298
  if (agent.reasoningEffort) out.AGENTCONNECT_REASONING_EFFORT = agent.reasoningEffort;
5295
5299
  if (agent.executionMode) out.AGENTCONNECT_EXECUTION_MODE = agent.executionMode;
5296
- if (agent.description) out.AGENTCONNECT_SYSTEM_PROMPT = agent.description;
5297
5300
  return out;
5298
5301
  }
5299
5302
  //#endregion
@@ -5925,6 +5928,13 @@ var SessionManager = class {
5925
5928
  });
5926
5929
  blocks.push(...attBlocks);
5927
5930
  }
5931
+ if (created) {
5932
+ const inlineSystemPrompt = host.inlineSystemPrompt?.();
5933
+ if (inlineSystemPrompt) blocks.unshift({
5934
+ type: "text",
5935
+ text: inlineSystemPrompt
5936
+ });
5937
+ }
5928
5938
  rec.lastDeliveredTs = ts;
5929
5939
  rec.state = "prompting";
5930
5940
  rec.updatedAt = Date.now();
@@ -62578,6 +62588,22 @@ function capOutput(s) {
62578
62588
  const t = s.trim();
62579
62589
  return t.length > MAX_TOOL_OUTPUT ? `${t.slice(0, MAX_TOOL_OUTPUT - 1)}…` : t;
62580
62590
  }
62591
+ /** The per-turn attribution footer: a Slack `context` block — small, secondary/gray text
62592
+ * reading "sent by agentconnect.md", the name linking to the Web App console (`webAppUrl`).
62593
+ * Context blocks use mrkdwn (`<url|text>`), not the CommonMark of `markdown` blocks. `text`
62594
+ * is the notification/accessibility fallback. */
62595
+ function attributionBlocks(webAppUrl) {
62596
+ return {
62597
+ text: "sent by agentconnect.md",
62598
+ blocks: [{
62599
+ type: "context",
62600
+ elements: [{
62601
+ type: "mrkdwn",
62602
+ text: `sent by <${webAppUrl}|agentconnect.md>`
62603
+ }]
62604
+ }]
62605
+ };
62606
+ }
62581
62607
  /** Pull human-readable output text out of an ACP tool_call/_update. Prefers the `content[]`
62582
62608
  * text blocks (the tool's reported output); falls back to a string `rawOutput`. diff /
62583
62609
  * terminal blocks and non-string rawOutput are skipped — they have no compact inline text. */
@@ -63017,12 +63043,20 @@ var OutputConverger = class {
63017
63043
  * — in medium/high — append a footer linking to the Web App session detail. The
63018
63044
  * link is omitted entirely when none is configured (no `local://` placeholder).
63019
63045
  */
63020
- onFinal(link) {
63046
+ onFinal(link, webAppUrl) {
63021
63047
  const clear = {
63022
63048
  kind: "set-status",
63023
63049
  text: ""
63024
63050
  };
63025
- if (this.mode === "low") return [...this.flush(), clear];
63051
+ const attribution = webAppUrl ? [{
63052
+ kind: "attribution",
63053
+ ...attributionBlocks(webAppUrl)
63054
+ }] : [];
63055
+ if (this.mode === "low") return [
63056
+ ...this.flush(),
63057
+ clear,
63058
+ ...attribution
63059
+ ];
63026
63060
  const reasoning = this.drainReasoning();
63027
63061
  const footer = link ? [{
63028
63062
  kind: "notice",
@@ -63032,7 +63066,8 @@ var OutputConverger = class {
63032
63066
  ...reasoning,
63033
63067
  ...this.flush(),
63034
63068
  clear,
63035
- ...footer
63069
+ ...footer,
63070
+ ...attribution
63036
63071
  ];
63037
63072
  }
63038
63073
  };
@@ -63252,6 +63287,22 @@ var SlackConnection = class {
63252
63287
  }))?.ts;
63253
63288
  });
63254
63289
  }
63290
+ /** Post a one-shot Block Kit `context` message — the per-turn "sent by agentconnect.md"
63291
+ * attribution footer (small secondary/gray text). Fire-and-forget: unlike the status bar
63292
+ * its ts is never kept for edits, so nothing is returned. `unfurl_*` off so the console
63293
+ * link doesn't sprout a preview card. */
63294
+ async postContext(channel, blocks, text, threadTs) {
63295
+ await this.queue.enqueue(async () => {
63296
+ await this.app.client.chat.postMessage({
63297
+ channel,
63298
+ thread_ts: threadTs,
63299
+ text,
63300
+ blocks,
63301
+ unfurl_links: false,
63302
+ unfurl_media: false
63303
+ });
63304
+ });
63305
+ }
63255
63306
  /** Edit a previously-posted Block Kit message in place (chat.update). Best-effort.
63256
63307
  * `unfurl_links: false` keeps an edit from unfurling the "View session" URL. */
63257
63308
  async updateBlocks(channel, ts, blocks, text) {
@@ -81833,7 +81884,10 @@ var Daemon = class Daemon {
81833
81884
  const skipped = Object.keys(resolvedRuntimes).filter((id) => !this.runtimes[id]);
81834
81885
  if (skipped.length) this.log.info(`runtimes not installed (skipped): ${skipped.join(", ")}`);
81835
81886
  this.store = new LocalStore(statePath(root));
81836
- this.nameResolver = new SlackNameResolver((id, name) => this.store.setDisplayName(id, name, Date.now()), this.log);
81887
+ this.nameResolver = new SlackNameResolver((id, name) => {
81888
+ this.store.setDisplayName(id, name, Date.now());
81889
+ this.emitSessionMetadataSnapshotsForDisplayName(id);
81890
+ }, this.log);
81837
81891
  this.cpRouting = new CpRoutingLayer({
81838
81892
  load: () => {
81839
81893
  const row = this.store.getCpRouting();
@@ -82125,7 +82179,11 @@ var Daemon = class Daemon {
82125
82179
  try {
82126
82180
  const channels = await conn.listBotChannels();
82127
82181
  if (!channels) return;
82128
- for (const c of channels) if (c.name) this.store.setDisplayName(c.id, c.name, Date.now());
82182
+ for (const c of channels) {
82183
+ if (!c.name) continue;
82184
+ this.store.setDisplayName(c.id, c.name, Date.now());
82185
+ this.emitSessionMetadataSnapshotsForDisplayName(c.id);
82186
+ }
82129
82187
  for (const [integrationId, c] of this.connByIntegration) {
82130
82188
  if (c !== conn) continue;
82131
82189
  this.channelSnapshots.set(integrationId, channels);
@@ -82158,7 +82216,8 @@ var Daemon = class Daemon {
82158
82216
  configPrefs: {
82159
82217
  model: agent.runtimeOverrides?.model,
82160
82218
  reasoningEffort: agent.reasoningEffort,
82161
- fastMode: agent.fastMode
82219
+ fastMode: agent.fastMode,
82220
+ systemPrompt: agent.description
82162
82221
  },
82163
82222
  log: this.log
82164
82223
  });
@@ -82656,7 +82715,7 @@ var Daemon = class Daemon {
82656
82715
  });
82657
82716
  } else {
82658
82717
  const link = this.sessionLink(sessionId);
82659
- for (const action of conv.onFinal(link)) this.enqueueApply(p, action);
82718
+ for (const action of conv.onFinal(link, this.webAppBase())) this.enqueueApply(p, action);
82660
82719
  }
82661
82720
  for (const ev of rec.onFinal()) this.recordEvent(agentId, msg.channel, statusThread, ev);
82662
82721
  await p.applyChain;
@@ -82756,6 +82815,9 @@ var Daemon = class Daemon {
82756
82815
  case "tool-output":
82757
82816
  await conn.postMessage(p.channel, action.text, p.thread);
82758
82817
  return;
82818
+ case "attribution":
82819
+ await conn.postContext(p.channel, action.blocks, action.text, p.thread);
82820
+ return;
82759
82821
  case "progress":
82760
82822
  if (p.progressTs) await conn.updateMessage(p.channel, p.progressTs, action.text);
82761
82823
  else if (!p.progressAttempted) {
@@ -82799,7 +82861,14 @@ var Daemon = class Daemon {
82799
82861
  * known; without it the link falls back to `<base>/sessions/<id>`. Always resolves, so
82800
82862
  * the View-session link is always available. */
82801
82863
  sessionLink(acpSessionId) {
82802
- return `${(this.cfg.webAppUrl ?? this.cpWebAppUrl ?? DEFAULT_WEB_APP_URL).replace(/\/$/, "")}${this.cpOrgSlug ? `/${encodeURIComponent(this.cpOrgSlug)}` : ""}/sessions/${acpSessionId}`;
82864
+ const orgSeg = this.cpOrgSlug ? `/${encodeURIComponent(this.cpOrgSlug)}` : "";
82865
+ return `${this.webAppBase()}${orgSeg}/sessions/${acpSessionId}`;
82866
+ }
82867
+ /** The Web App console origin (trailing slash stripped): the explicit local `webAppUrl`,
82868
+ * else the CP-provided origin, else the public default. Backs both the session deep link
82869
+ * and the per-turn "sent by agentconnect.md" attribution footer. Always resolves. */
82870
+ webAppBase() {
82871
+ return (this.cfg.webAppUrl ?? this.cpWebAppUrl ?? DEFAULT_WEB_APP_URL).replace(/\/$/, "");
82803
82872
  }
82804
82873
  sessionListProjection(sessionId, agentId) {
82805
82874
  return createSessionReader(this.store, (id) => this.replyConnFor(id)?.workspaceUrl).list({ agentId }).sessions.find((s) => s.sessionId === sessionId);
@@ -82838,6 +82907,21 @@ var Daemon = class Daemon {
82838
82907
  this.log.debug(`event/session emit failed (session ${input.sessionId}): ${err.message}`);
82839
82908
  }
82840
82909
  }
82910
+ emitSessionMetadataSnapshotsForDisplayName(id) {
82911
+ if (!this.cpClient) return;
82912
+ for (const row of this.store.listSessions()) {
82913
+ if (!row.acpSessionId) continue;
82914
+ if (row.channel !== id && row.triggeredBy !== id) continue;
82915
+ this.emitSessionMetadataSnapshot({
82916
+ sessionId: row.acpSessionId,
82917
+ agentId: row.agentId,
82918
+ phase: "plan",
82919
+ platform: row.platform,
82920
+ channel: row.channel,
82921
+ thread: row.thread
82922
+ });
82923
+ }
82924
+ }
82841
82925
  /** Assemble a status snapshot from the parts (agent config + host model selector +
82842
82926
  * folded ACP usage). Shared by `buildStatusInfo` (per-turn Pending) and
82843
82927
  * `statusInfoForKey` (the ⚙-modal, keyed by session). */
@@ -82951,7 +83035,17 @@ var Daemon = class Daemon {
82951
83035
  });
82952
83036
  this.emitStatusBar(p);
82953
83037
  }
82954
- if (update?.sessionUpdate === "session_info_update" && update.title !== void 0) this.store.setSessionTitle(p.sessionKey, update.title);
83038
+ if (update?.sessionUpdate === "session_info_update" && update.title !== void 0) {
83039
+ this.store.setSessionTitle(p.sessionKey, update.title);
83040
+ this.emitSessionMetadataSnapshot({
83041
+ sessionId,
83042
+ agentId: p.agentId,
83043
+ phase: "plan",
83044
+ platform: p.platform,
83045
+ channel: p.channel,
83046
+ thread: p.statusThread
83047
+ });
83048
+ }
82955
83049
  if (p.webchat) this.emitWebchatUpdate(p, update);
82956
83050
  else {
82957
83051
  for (const action of p.conv.onUpdate(update)) this.enqueueApply(p, action);
@@ -83603,4 +83697,4 @@ async function runForeground(opts, deps = {}) {
83603
83697
  //#endregion
83604
83698
  export { ClientTransport as n, runForeground as t };
83605
83699
 
83606
- //# sourceMappingURL=run-foreground-CD2uFFvp.js.map
83700
+ //# sourceMappingURL=run-foreground-BroK115h.js.map