acpx 0.1.7 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Janitr AI
3
+ Copyright (c) 2025 OpenClaw Team
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -6,10 +6,13 @@
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/acpx.svg)](https://www.npmjs.com/package/acpx)
8
8
  [![npm downloads](https://img.shields.io/npm/dm/acpx.svg)](https://www.npmjs.com/package/acpx)
9
- [![CI](https://github.com/janitrai/acpx/actions/workflows/ci.yml/badge.svg)](https://github.com/janitrai/acpx/actions/workflows/ci.yml)
9
+ [![CI](https://github.com/openclaw/acpx/actions/workflows/ci.yml/badge.svg)](https://github.com/openclaw/acpx/actions/workflows/ci.yml)
10
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
11
11
  [![Node.js](https://img.shields.io/node/v/acpx.svg)](https://nodejs.org)
12
12
 
13
+ > Alpha notice: `acpx` is in alpha and the CLI/runtime interfaces are likely to change.
14
+ > ACP coverage status: see [ACP Spec Coverage Roadmap](docs/2026-02-19-acp-coverage-roadmap.md).
15
+
13
16
  Your agents love acpx! 🤖❤️ They hate having to scrape characters from a PTY session 😤
14
17
 
15
18
  `acpx` is a headless CLI client for the [Agent Client Protocol (ACP)](https://agentclientprotocol.com), so AI agents and orchestrators can talk to coding agents over a structured protocol instead of PTY scraping.
@@ -84,10 +87,10 @@ I want you to use acpx to run coding agents over the Agent Client Protocol
84
87
 
85
88
  3. Read the acpx skill reference so you know every command, flag, and
86
89
  workflow pattern:
87
- https://raw.githubusercontent.com/janitrai/acpx/main/skills/acpx/SKILL.md
90
+ https://raw.githubusercontent.com/openclaw/acpx/main/skills/acpx/SKILL.md
88
91
 
89
92
  4. If you need the full CLI reference with all options and examples:
90
- https://raw.githubusercontent.com/janitrai/acpx/main/docs/CLI.md
93
+ https://raw.githubusercontent.com/openclaw/acpx/main/docs/CLI.md
91
94
 
92
95
  From now on, when you need to delegate work to a coding agent, use acpx
93
96
  instead of spawning raw terminal sessions. For example:
@@ -257,6 +260,9 @@ JSON events include a stable envelope for correlation:
257
260
  }
258
261
  ```
259
262
 
263
+ Session-control JSON payloads (`sessions new|ensure`, `status`) may also include
264
+ `runtimeSessionId` when the adapter exposes a provider-native session ID.
265
+
260
266
  ## Built-in agents and custom servers
261
267
 
262
268
  Built-ins:
package/dist/cli.d.ts CHANGED
@@ -10,6 +10,7 @@ type SessionHistoryEntry = {
10
10
  type SessionRecord = {
11
11
  id: string;
12
12
  sessionId: string;
13
+ agentSessionId?: string;
13
14
  agentCommand: string;
14
15
  cwd: string;
15
16
  name?: string;
package/dist/cli.js CHANGED
@@ -1715,6 +1715,35 @@ function classifyPermissionDecision(params, response) {
1715
1715
  return "denied";
1716
1716
  }
1717
1717
 
1718
+ // src/agent-session-id.ts
1719
+ var AGENT_SESSION_ID_META_KEYS = ["agentSessionId", "sessionId"];
1720
+ function normalizeAgentSessionId(value) {
1721
+ if (typeof value !== "string") {
1722
+ return void 0;
1723
+ }
1724
+ const trimmed = value.trim();
1725
+ return trimmed.length > 0 ? trimmed : void 0;
1726
+ }
1727
+ function asMetaRecord(meta) {
1728
+ if (!meta || typeof meta !== "object" || Array.isArray(meta)) {
1729
+ return void 0;
1730
+ }
1731
+ return meta;
1732
+ }
1733
+ function extractAgentSessionId(meta) {
1734
+ const record = asMetaRecord(meta);
1735
+ if (!record) {
1736
+ return void 0;
1737
+ }
1738
+ for (const key of AGENT_SESSION_ID_META_KEYS) {
1739
+ const normalized = normalizeAgentSessionId(record[key]);
1740
+ if (normalized) {
1741
+ return normalized;
1742
+ }
1743
+ }
1744
+ return void 0;
1745
+ }
1746
+
1718
1747
  // src/terminal.ts
1719
1748
  import { spawn } from "child_process";
1720
1749
  import { randomUUID } from "crypto";
@@ -2373,18 +2402,22 @@ var AcpClient = class {
2373
2402
  cwd: asAbsoluteCwd(cwd),
2374
2403
  mcpServers: []
2375
2404
  });
2376
- return result.sessionId;
2405
+ return {
2406
+ sessionId: result.sessionId,
2407
+ agentSessionId: extractAgentSessionId(result._meta)
2408
+ };
2377
2409
  }
2378
2410
  async loadSession(sessionId, cwd = this.options.cwd) {
2379
2411
  this.getConnection();
2380
- await this.loadSessionWithOptions(sessionId, cwd, {});
2412
+ return await this.loadSessionWithOptions(sessionId, cwd, {});
2381
2413
  }
2382
2414
  async loadSessionWithOptions(sessionId, cwd = this.options.cwd, options = {}) {
2383
2415
  const connection = this.getConnection();
2384
2416
  const previousSuppression = this.suppressSessionUpdates;
2385
2417
  this.suppressSessionUpdates = previousSuppression || Boolean(options.suppressReplayUpdates);
2418
+ let response;
2386
2419
  try {
2387
- await connection.loadSession({
2420
+ response = await connection.loadSession({
2388
2421
  sessionId,
2389
2422
  cwd: asAbsoluteCwd(cwd),
2390
2423
  mcpServers: []
@@ -2396,6 +2429,9 @@ var AcpClient = class {
2396
2429
  } finally {
2397
2430
  this.suppressSessionUpdates = previousSuppression;
2398
2431
  }
2432
+ return {
2433
+ agentSessionId: extractAgentSessionId(response?._meta)
2434
+ };
2399
2435
  }
2400
2436
  async prompt(sessionId, text) {
2401
2437
  const connection = this.getConnection();
@@ -4203,6 +4239,7 @@ function parseSessionRecord(raw) {
4203
4239
  return null;
4204
4240
  }
4205
4241
  const record = raw;
4242
+ const agentSessionId = normalizeAgentSessionId(record.agentSessionId);
4206
4243
  const name = record.name == null ? void 0 : typeof record.name === "string" && record.name.trim().length > 0 ? record.name.trim() : null;
4207
4244
  const pid = record.pid == null ? void 0 : Number.isInteger(record.pid) && record.pid > 0 ? record.pid : null;
4208
4245
  const closed = record.closed == null ? false : typeof record.closed === "boolean" ? record.closed : null;
@@ -4225,6 +4262,7 @@ function parseSessionRecord(raw) {
4225
4262
  ...record,
4226
4263
  id: record.id,
4227
4264
  sessionId: record.sessionId,
4265
+ agentSessionId,
4228
4266
  agentCommand: record.agentCommand,
4229
4267
  cwd: record.cwd,
4230
4268
  name,
@@ -4617,6 +4655,13 @@ function applyLifecycleSnapshotToRecord(record, snapshot) {
4617
4655
  record.lastAgentExitAt = void 0;
4618
4656
  record.lastAgentDisconnectReason = void 0;
4619
4657
  }
4658
+ function reconcileAgentSessionId(record, agentSessionId) {
4659
+ const normalized = normalizeAgentSessionId(agentSessionId);
4660
+ if (!normalized) {
4661
+ return;
4662
+ }
4663
+ record.agentSessionId = normalized;
4664
+ }
4620
4665
  function shouldFallbackToNewSession(error) {
4621
4666
  if (error instanceof TimeoutError || error instanceof InterruptedError) {
4622
4667
  return false;
@@ -4653,31 +4698,40 @@ async function connectAndLoadSession(options) {
4653
4698
  let sessionId = record.sessionId;
4654
4699
  if (client.supportsLoadSession()) {
4655
4700
  try {
4656
- await withTimeout(
4701
+ const loadResult = await withTimeout(
4657
4702
  client.loadSessionWithOptions(record.sessionId, record.cwd, {
4658
4703
  suppressReplayUpdates: true
4659
4704
  }),
4660
4705
  options.timeoutMs
4661
4706
  );
4707
+ reconcileAgentSessionId(record, loadResult.agentSessionId);
4662
4708
  resumed = true;
4663
4709
  } catch (error) {
4664
4710
  loadError = formatErrorMessage(error);
4665
4711
  if (!shouldFallbackToNewSession(error)) {
4666
4712
  throw error;
4667
4713
  }
4668
- sessionId = await withTimeout(
4714
+ const createdSession = await withTimeout(
4669
4715
  client.createSession(record.cwd),
4670
4716
  options.timeoutMs
4671
4717
  );
4718
+ sessionId = createdSession.sessionId;
4672
4719
  record.sessionId = sessionId;
4720
+ reconcileAgentSessionId(record, createdSession.agentSessionId);
4673
4721
  }
4674
4722
  } else {
4675
- sessionId = await withTimeout(client.createSession(record.cwd), options.timeoutMs);
4723
+ const createdSession = await withTimeout(
4724
+ client.createSession(record.cwd),
4725
+ options.timeoutMs
4726
+ );
4727
+ sessionId = createdSession.sessionId;
4676
4728
  record.sessionId = sessionId;
4729
+ reconcileAgentSessionId(record, createdSession.agentSessionId);
4677
4730
  }
4678
4731
  options.onSessionIdResolved?.(sessionId);
4679
4732
  return {
4680
4733
  sessionId,
4734
+ agentSessionId: record.agentSessionId,
4681
4735
  resumed,
4682
4736
  loadError
4683
4737
  };
@@ -5030,10 +5084,11 @@ async function runOnce(options) {
5030
5084
  return await withInterrupt(
5031
5085
  async () => {
5032
5086
  await withTimeout(client.start(), options.timeoutMs);
5033
- const sessionId = await withTimeout(
5087
+ const createdSession = await withTimeout(
5034
5088
  client.createSession(absolutePath(options.cwd)),
5035
5089
  options.timeoutMs
5036
5090
  );
5091
+ const sessionId = createdSession.sessionId;
5037
5092
  output.setContext({
5038
5093
  sessionId,
5039
5094
  stream: "prompt"
@@ -5069,15 +5124,17 @@ async function createSession(options) {
5069
5124
  return await withInterrupt(
5070
5125
  async () => {
5071
5126
  await withTimeout(client.start(), options.timeoutMs);
5072
- const sessionId = await withTimeout(
5127
+ const createdSession = await withTimeout(
5073
5128
  client.createSession(absolutePath(options.cwd)),
5074
5129
  options.timeoutMs
5075
5130
  );
5131
+ const sessionId = createdSession.sessionId;
5076
5132
  const lifecycle = client.getAgentLifecycleSnapshot();
5077
5133
  const now = isoNow2();
5078
5134
  const record = {
5079
5135
  id: sessionId,
5080
5136
  sessionId,
5137
+ agentSessionId: createdSession.agentSessionId,
5081
5138
  agentCommand: options.agentCommand,
5082
5139
  cwd: absolutePath(options.cwd),
5083
5140
  name: normalizeName(options.name),
@@ -5655,8 +5712,22 @@ function resolveAgentInvocation(explicitAgentName, globalFlags, config) {
5655
5712
  }
5656
5713
  function printSessionsByFormat(sessions, format) {
5657
5714
  if (format === "json") {
5658
- process.stdout.write(`${JSON.stringify(sessions)}
5659
- `);
5715
+ process.stdout.write(
5716
+ `${JSON.stringify(
5717
+ sessions.map((session) => {
5718
+ const { id, sessionId, agentSessionId, ...rest } = session;
5719
+ return {
5720
+ ...rest,
5721
+ ...canonicalSessionIdentity({
5722
+ id,
5723
+ sessionId,
5724
+ agentSessionId
5725
+ })
5726
+ };
5727
+ })
5728
+ )}
5729
+ `
5730
+ );
5660
5731
  return;
5661
5732
  }
5662
5733
  if (format === "quiet") {
@@ -5684,8 +5755,7 @@ function printClosedSessionByFormat(record, format) {
5684
5755
  process.stdout.write(
5685
5756
  `${JSON.stringify({
5686
5757
  type: "session_closed",
5687
- id: record.id,
5688
- sessionId: record.sessionId,
5758
+ ...canonicalSessionIdentity(record),
5689
5759
  name: record.name
5690
5760
  })}
5691
5761
  `
@@ -5698,15 +5768,22 @@ function printClosedSessionByFormat(record, format) {
5698
5768
  process.stdout.write(`${record.id}
5699
5769
  `);
5700
5770
  }
5771
+ function canonicalSessionIdentity(record) {
5772
+ const normalizedAgentSessionId = normalizeAgentSessionId(record.agentSessionId);
5773
+ return {
5774
+ acpxRecordId: record.id,
5775
+ acpxSessionId: record.sessionId,
5776
+ ...normalizedAgentSessionId ? { agentSessionId: normalizedAgentSessionId } : {}
5777
+ };
5778
+ }
5701
5779
  function printNewSessionByFormat(record, replaced, format) {
5702
5780
  if (format === "json") {
5703
5781
  process.stdout.write(
5704
5782
  `${JSON.stringify({
5705
5783
  type: "session_created",
5706
- id: record.id,
5707
- sessionId: record.sessionId,
5784
+ ...canonicalSessionIdentity(record),
5708
5785
  name: record.name,
5709
- replacedSessionId: replaced?.id
5786
+ ...replaced ? { replacedAcpxRecordId: replaced.id } : {}
5710
5787
  })}
5711
5788
  `
5712
5789
  );
@@ -5730,8 +5807,7 @@ function printEnsuredSessionByFormat(record, created, format) {
5730
5807
  process.stdout.write(
5731
5808
  `${JSON.stringify({
5732
5809
  type: "session_ensured",
5733
- id: record.id,
5734
- sessionId: record.sessionId,
5810
+ ...canonicalSessionIdentity(record),
5735
5811
  name: record.name,
5736
5812
  created
5737
5813
  })}
@@ -5753,7 +5829,7 @@ function printQueuedPromptByFormat(result, format) {
5753
5829
  process.stdout.write(
5754
5830
  `${JSON.stringify({
5755
5831
  type: "queued",
5756
- sessionId: result.sessionId,
5832
+ acpxRecordId: result.sessionId,
5757
5833
  requestId: result.requestId
5758
5834
  })}
5759
5835
  `
@@ -5910,8 +5986,13 @@ async function handleExec(explicitAgentName, promptParts, flags, command, config
5910
5986
  }
5911
5987
  function printCancelResultByFormat(result, format) {
5912
5988
  if (format === "json") {
5913
- process.stdout.write(`${JSON.stringify(result)}
5914
- `);
5989
+ process.stdout.write(
5990
+ `${JSON.stringify({
5991
+ acpxRecordId: result.sessionId || null,
5992
+ cancelled: result.cancelled
5993
+ })}
5994
+ `
5995
+ );
5915
5996
  return;
5916
5997
  }
5917
5998
  if (result.cancelled) {
@@ -5924,7 +6005,7 @@ function printSetModeResultByFormat(modeId, result, format) {
5924
6005
  if (format === "json") {
5925
6006
  process.stdout.write(
5926
6007
  `${JSON.stringify({
5927
- sessionId: result.record.id,
6008
+ ...canonicalSessionIdentity(result.record),
5928
6009
  modeId,
5929
6010
  resumed: result.resumed
5930
6011
  })}
@@ -5944,7 +6025,7 @@ function printSetConfigOptionResultByFormat(configId, value, result, format) {
5944
6025
  if (format === "json") {
5945
6026
  process.stdout.write(
5946
6027
  `${JSON.stringify({
5947
- sessionId: result.record.id,
6028
+ ...canonicalSessionIdentity(result.record),
5948
6029
  configId,
5949
6030
  value,
5950
6031
  resumed: result.resumed,
@@ -6136,8 +6217,18 @@ async function handleSessionsEnsure(explicitAgentName, flags, command, config) {
6136
6217
  }
6137
6218
  function printSessionDetailsByFormat(record, format) {
6138
6219
  if (format === "json") {
6139
- process.stdout.write(`${JSON.stringify(record)}
6140
- `);
6220
+ const { id, sessionId, agentSessionId, ...rest } = record;
6221
+ process.stdout.write(
6222
+ `${JSON.stringify({
6223
+ ...rest,
6224
+ ...canonicalSessionIdentity({
6225
+ id,
6226
+ sessionId,
6227
+ agentSessionId
6228
+ })
6229
+ })}
6230
+ `
6231
+ );
6141
6232
  return;
6142
6233
  }
6143
6234
  if (format === "quiet") {
@@ -6145,9 +6236,11 @@ function printSessionDetailsByFormat(record, format) {
6145
6236
  `);
6146
6237
  return;
6147
6238
  }
6148
- process.stdout.write(`id: ${record.id}
6239
+ process.stdout.write(`acpxRecordId: ${record.id}
6240
+ `);
6241
+ process.stdout.write(`acpxSessionId: ${record.sessionId}
6149
6242
  `);
6150
- process.stdout.write(`sessionId: ${record.sessionId}
6243
+ process.stdout.write(`agentSessionId: ${record.agentSessionId ?? "-"}
6151
6244
  `);
6152
6245
  process.stdout.write(`agent: ${record.agentCommand}
6153
6246
  `);
@@ -6188,8 +6281,7 @@ function printSessionHistoryByFormat(record, limit, format) {
6188
6281
  if (format === "json") {
6189
6282
  process.stdout.write(
6190
6283
  `${JSON.stringify({
6191
- id: record.id,
6192
- sessionId: record.sessionId,
6284
+ ...canonicalSessionIdentity(record),
6193
6285
  limit,
6194
6286
  count: visible.length,
6195
6287
  entries: visible
@@ -6275,7 +6367,9 @@ async function handleStatus(explicitAgentName, flags, command, config) {
6275
6367
  });
6276
6368
  if (!record) {
6277
6369
  const payload2 = {
6278
- sessionId: null,
6370
+ acpxRecordId: null,
6371
+ acpxSessionId: null,
6372
+ agentSessionId: null,
6279
6373
  agentCommand: agent.agentCommand,
6280
6374
  pid: null,
6281
6375
  status: "no-session",
@@ -6309,7 +6403,7 @@ async function handleStatus(explicitAgentName, flags, command, config) {
6309
6403
  }
6310
6404
  const running = isProcessAlive(record.pid);
6311
6405
  const payload = {
6312
- sessionId: record.id,
6406
+ ...canonicalSessionIdentity(record),
6313
6407
  agentCommand: record.agentCommand,
6314
6408
  pid: record.pid ?? null,
6315
6409
  status: running ? "running" : "dead",
@@ -6328,7 +6422,11 @@ async function handleStatus(explicitAgentName, flags, command, config) {
6328
6422
  `);
6329
6423
  return;
6330
6424
  }
6331
- process.stdout.write(`session: ${payload.sessionId}
6425
+ process.stdout.write(`acpxRecordId: ${payload.acpxRecordId}
6426
+ `);
6427
+ process.stdout.write(`acpxSessionId: ${payload.acpxSessionId}
6428
+ `);
6429
+ process.stdout.write(`agentSessionId: ${payload.agentSessionId ?? "-"}
6332
6430
  `);
6333
6431
  process.stdout.write(`agent: ${payload.agentCommand}
6334
6432
  `);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "acpx",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Headless CLI client for the Agent Client Protocol (ACP) — talk to coding agents from the command line",
5
5
  "type": "module",
6
6
  "files": [
@@ -36,11 +36,11 @@
36
36
  "coding-agent",
37
37
  "ai"
38
38
  ],
39
- "author": "Janitr AI",
39
+ "author": "",
40
40
  "license": "MIT",
41
41
  "repository": {
42
42
  "type": "git",
43
- "url": "git+https://github.com/janitrai/acpx.git"
43
+ "url": "git+https://github.com/openclaw/acpx.git"
44
44
  },
45
45
  "engines": {
46
46
  "node": ">=18"