@kin-tio/cli 0.6.2 → 0.7.1

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.
@@ -207,10 +207,19 @@ export class CodexAppServer {
207
207
  this.#pending.delete(message.id);
208
208
  const rpcError = asRecord(message.error);
209
209
  if (rpcError) {
210
- const error = new Error(`Codex app-server request failed: ${pending.method}`);
211
- if (typeof rpcError.code === 'number' && Number.isSafeInteger(rpcError.code)) {
212
- error.code = rpcError.code;
213
- }
210
+ const code = typeof rpcError.code === 'number' && Number.isSafeInteger(rpcError.code)
211
+ ? rpcError.code
212
+ : undefined;
213
+ const errorData = asRecord(rpcError.data);
214
+ const category = codexFailureLabel(errorData?.codexErrorInfo ?? rpcError.codexErrorInfo);
215
+ const diagnostic = [
216
+ ...(code === undefined ? [] : [`code ${code}`]),
217
+ ...(category ? [`category ${category}`] : []),
218
+ ];
219
+ const error = new Error(`Codex app-server request failed: ${pending.method}` +
220
+ (diagnostic.length ? ` (${diagnostic.join('; ')})` : ''));
221
+ if (code !== undefined)
222
+ error.code = code;
214
223
  pending.reject(error);
215
224
  }
216
225
  else {
@@ -355,8 +364,10 @@ class CodexAppServerThread {
355
364
  #params() {
356
365
  return {
357
366
  cwd: this.#options.workingDirectory,
358
- approvalPolicy: this.#options.approvalPolicy,
359
- sandbox: 'read-only',
367
+ ...(this.#options.approvalPolicy
368
+ ? { approvalPolicy: this.#options.approvalPolicy }
369
+ : {}),
370
+ ...(this.#options.sandbox ? { sandbox: this.#options.sandbox } : {}),
360
371
  ...(this.#options.developerInstructions
361
372
  ? { developerInstructions: this.#options.developerInstructions }
362
373
  : {}),
@@ -103,6 +103,9 @@ export class ConversationProcessor {
103
103
  #conversationKey(record) {
104
104
  return `${record.channel}\0${record.accountKey}\0${record.peerId}`;
105
105
  }
106
+ #agentAccess(record) {
107
+ return this.#pipeline.agentAccess?.(record) === 'host' ? 'host' : 'restricted';
108
+ }
106
109
  #notifyQueued(record) {
107
110
  const key = this.#conversationKey(record);
108
111
  if (this.#queueNotified.has(key))
@@ -304,6 +307,7 @@ export class ConversationProcessor {
304
307
  }
305
308
  async #submit(record, input, options = {}) {
306
309
  const opaqueConversationId = conversationId(record);
310
+ const agentAccess = this.#agentAccess(record);
307
311
  const activePrimary = options.wait
308
312
  ? undefined
309
313
  : this.#pipeline.agent.activePrimary(opaqueConversationId);
@@ -324,6 +328,7 @@ export class ConversationProcessor {
324
328
  try {
325
329
  const submission = await this.#pipeline.agent.submit({
326
330
  ...input,
331
+ agentAccess,
327
332
  channel: record.channel,
328
333
  mode: 'steer',
329
334
  conversationId: opaqueConversationId,
@@ -359,7 +364,7 @@ export class ConversationProcessor {
359
364
  });
360
365
  const boundaryMessageKey = options.boundaryMessageKey || record.messageKey;
361
366
  const conversationBefore = this.#store.getConversation(record.channel, record.accountKey, record.peerId);
362
- const ensuredThreadId = await this.#pipeline.agent.ensureThread(opaqueConversationId, conversationBefore?.threadId || '');
367
+ const ensuredThreadId = await this.#pipeline.agent.ensureThread(opaqueConversationId, conversationBefore?.threadId || '', agentAccess);
363
368
  const pendingMemoryThreadId = this.#pipeline.agent.takePendingMemoryThread?.(opaqueConversationId) || '';
364
369
  if (!conversationBefore || ensuredThreadId !== conversationBefore.threadId) {
365
370
  this.#store.setConversationThread({
@@ -389,6 +394,7 @@ export class ConversationProcessor {
389
394
  try {
390
395
  submission = await this.#pipeline.agent.submit({
391
396
  ...input,
397
+ agentAccess,
392
398
  channel: record.channel,
393
399
  ...(artifactCatalog.length ? { artifactCatalog } : {}),
394
400
  mode: 'start',
@@ -668,7 +674,7 @@ export class ConversationProcessor {
668
674
  const latestId = ids.at(-1) || primary.clientInputId;
669
675
  const steering = validGroup.filter((item) => item.status === 'steering');
670
676
  const inspection = conversation?.threadId && this.#pipeline.agent.inspectHistory
671
- ? await this.#pipeline.agent.inspectHistory(conversation.threadId, ids, latestId)
677
+ ? await this.#pipeline.agent.inspectHistory(conversation.threadId, ids, latestId, this.#agentAccess(primary))
672
678
  : undefined;
673
679
  const missingInput = steering.some((record) => {
674
680
  const clientId = record.clientInputId || record.messageKey;
@@ -3,7 +3,7 @@ import fs from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { MAX_WECHAT_IMAGE_BYTES, detectImageFormat, } from '../lib/image-format.js';
5
5
  import { ensurePrivateDirectory } from '../lib/private-directory.js';
6
- const SCHEMA_VERSION = 22;
6
+ const SCHEMA_VERSION = 24;
7
7
  const INBOUND_STATUSES = [
8
8
  'received',
9
9
  'processing',
@@ -327,8 +327,8 @@ export class SqliteStore {
327
327
  if (version !== 0 && version !== 11 && version !== 12 &&
328
328
  version !== 13 && version !== 14 && version !== 15 &&
329
329
  version !== 16 && version !== 17 && version !== 18 && version !== 19 &&
330
- version !== 20 && version !== 21) {
331
- throw new Error(`SQLite schema version ${version} is no longer supported; migrate to version 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, or 21 first`);
330
+ version !== 20 && version !== 21 && version !== 22 && version !== 23) {
331
+ throw new Error(`SQLite schema version ${version} is no longer supported; migrate to version 11 through 23 first`);
332
332
  }
333
333
  if (version === 11) {
334
334
  this.#database.exec(`
@@ -1384,6 +1384,131 @@ export class SqliteStore {
1384
1384
  PRAGMA foreign_keys = ON;
1385
1385
  `);
1386
1386
  }
1387
+ version = 22;
1388
+ }
1389
+ if (version === 22) {
1390
+ this.#database.exec('BEGIN IMMEDIATE');
1391
+ try {
1392
+ const accountColumns = this.#database.prepare('PRAGMA table_info(ilink_accounts)').all();
1393
+ if (!accountColumns.some(({ name }) => name === 'agent_access')) {
1394
+ this.#database.exec(`
1395
+ ALTER TABLE ilink_accounts ADD COLUMN agent_access TEXT NOT NULL
1396
+ DEFAULT 'restricted'
1397
+ CHECK (agent_access IN ('restricted', 'host'));
1398
+ `);
1399
+ }
1400
+ this.#database.exec(`
1401
+ DROP INDEX ilink_one_pending_offer_idx;
1402
+ ALTER TABLE ilink_login_offers RENAME TO ilink_login_offers_v22;
1403
+ CREATE TABLE ilink_login_offers (
1404
+ offer_id TEXT PRIMARY KEY,
1405
+ initiator_kind TEXT NOT NULL
1406
+ CHECK (initiator_kind IN ('local_operator', 'remote_adapter')),
1407
+ source_channel TEXT NOT NULL,
1408
+ source_message_key TEXT NOT NULL,
1409
+ source_account_id TEXT NOT NULL,
1410
+ source_peer_id TEXT NOT NULL,
1411
+ candidate_account_keys_json TEXT NOT NULL DEFAULT '[]'
1412
+ CHECK (json_valid(candidate_account_keys_json)),
1413
+ secret_generation INTEGER NOT NULL CHECK (secret_generation >= 0),
1414
+ nonce TEXT NOT NULL,
1415
+ ciphertext TEXT NOT NULL,
1416
+ auth_tag TEXT NOT NULL,
1417
+ api_base_url TEXT NOT NULL,
1418
+ status TEXT NOT NULL DEFAULT 'waiting'
1419
+ CHECK (status IN (
1420
+ 'waiting', 'scanned', 'confirmed', 'expired', 'failed', 'cancelled'
1421
+ )),
1422
+ expires_at INTEGER NOT NULL,
1423
+ last_polled_at INTEGER NOT NULL DEFAULT 0,
1424
+ error_code TEXT NOT NULL DEFAULT '',
1425
+ created_at INTEGER NOT NULL,
1426
+ updated_at INTEGER NOT NULL
1427
+ ) STRICT, WITHOUT ROWID;
1428
+ INSERT INTO ilink_login_offers (
1429
+ offer_id, initiator_kind, source_channel, source_message_key,
1430
+ source_account_id, source_peer_id, secret_generation,
1431
+ candidate_account_keys_json,
1432
+ nonce, ciphertext, auth_tag, api_base_url, status,
1433
+ expires_at, last_polled_at, error_code, created_at, updated_at
1434
+ )
1435
+ SELECT
1436
+ offer_id, 'remote_adapter', 'wechat_kf', source_message_key,
1437
+ source_open_kfid, source_external_userid, secret_generation,
1438
+ '[]',
1439
+ nonce, ciphertext, auth_tag, api_base_url, status,
1440
+ expires_at, last_polled_at, error_code, created_at, updated_at
1441
+ FROM ilink_login_offers_v22;
1442
+ DROP TABLE ilink_login_offers_v22;
1443
+ CREATE UNIQUE INDEX ilink_one_pending_offer_idx
1444
+ ON ilink_login_offers(
1445
+ source_channel, source_account_id, source_peer_id
1446
+ )
1447
+ WHERE status IN ('waiting', 'scanned');
1448
+
1449
+ ALTER TABLE ilink_enrollment_audit RENAME TO ilink_enrollment_audit_v22;
1450
+ CREATE TABLE ilink_enrollment_audit (
1451
+ offer_id TEXT PRIMARY KEY,
1452
+ initiator_kind TEXT NOT NULL
1453
+ CHECK (initiator_kind IN ('local_operator', 'remote_adapter')),
1454
+ source_channel TEXT NOT NULL,
1455
+ source_message_key TEXT NOT NULL,
1456
+ source_account_id TEXT NOT NULL,
1457
+ source_peer_id TEXT NOT NULL,
1458
+ account_key TEXT NOT NULL DEFAULT '',
1459
+ result TEXT NOT NULL CHECK (result IN (
1460
+ 'confirmed', 'expired', 'failed', 'cancelled',
1461
+ 'already_connected', 'verification_required'
1462
+ )),
1463
+ offered_at INTEGER NOT NULL,
1464
+ completed_at INTEGER NOT NULL
1465
+ ) STRICT, WITHOUT ROWID;
1466
+ INSERT INTO ilink_enrollment_audit (
1467
+ offer_id, initiator_kind, source_channel, source_message_key,
1468
+ source_account_id, source_peer_id, account_key,
1469
+ result, offered_at, completed_at
1470
+ )
1471
+ SELECT
1472
+ offer_id, 'remote_adapter', 'wechat_kf', source_message_key,
1473
+ source_open_kfid, source_external_userid, account_key,
1474
+ result, offered_at, completed_at
1475
+ FROM ilink_enrollment_audit_v22;
1476
+ DROP TABLE ilink_enrollment_audit_v22;
1477
+
1478
+ PRAGMA user_version = 23;
1479
+ COMMIT;
1480
+ `);
1481
+ }
1482
+ catch (error) {
1483
+ this.#database.exec('ROLLBACK');
1484
+ throw error;
1485
+ }
1486
+ version = 23;
1487
+ }
1488
+ if (version === 23) {
1489
+ this.#database.exec('BEGIN IMMEDIATE');
1490
+ try {
1491
+ const accountColumns = this.#database.prepare('PRAGMA table_info(ilink_accounts)').all();
1492
+ if (!accountColumns.some(({ name }) => name === 'runtime_enabled')) {
1493
+ this.#database.exec(`
1494
+ ALTER TABLE ilink_accounts ADD COLUMN runtime_enabled INTEGER NOT NULL
1495
+ DEFAULT 0 CHECK (runtime_enabled IN (0, 1));
1496
+ `);
1497
+ }
1498
+ this.#database.exec(`
1499
+ UPDATE ilink_accounts
1500
+ SET runtime_enabled = 1
1501
+ WHERE status = 'active' AND (
1502
+ SELECT COUNT(*) FROM ilink_accounts WHERE status = 'active'
1503
+ ) = 1;
1504
+ PRAGMA user_version = 24;
1505
+ COMMIT;
1506
+ `);
1507
+ }
1508
+ catch (error) {
1509
+ this.#database.exec('ROLLBACK');
1510
+ throw error;
1511
+ }
1387
1512
  return;
1388
1513
  }
1389
1514
  this.#database.exec('BEGIN IMMEDIATE');
@@ -1425,6 +1550,10 @@ export class SqliteStore {
1425
1550
  generation INTEGER NOT NULL DEFAULT 1 CHECK (generation > 0),
1426
1551
  status TEXT NOT NULL DEFAULT 'active'
1427
1552
  CHECK (status IN ('active', 'paused', 'disabled', 'revoked')),
1553
+ agent_access TEXT NOT NULL DEFAULT 'restricted'
1554
+ CHECK (agent_access IN ('restricted', 'host')),
1555
+ runtime_enabled INTEGER NOT NULL DEFAULT 0
1556
+ CHECK (runtime_enabled IN (0, 1)),
1428
1557
  pause_until INTEGER NOT NULL DEFAULT 0,
1429
1558
  cursor TEXT NOT NULL DEFAULT '',
1430
1559
  cursor_updated_at INTEGER NOT NULL DEFAULT 0,
@@ -1448,9 +1577,14 @@ export class SqliteStore {
1448
1577
 
1449
1578
  CREATE TABLE ilink_login_offers (
1450
1579
  offer_id TEXT PRIMARY KEY,
1580
+ initiator_kind TEXT NOT NULL
1581
+ CHECK (initiator_kind IN ('local_operator', 'remote_adapter')),
1582
+ source_channel TEXT NOT NULL,
1451
1583
  source_message_key TEXT NOT NULL,
1452
- source_open_kfid TEXT NOT NULL,
1453
- source_external_userid TEXT NOT NULL,
1584
+ source_account_id TEXT NOT NULL,
1585
+ source_peer_id TEXT NOT NULL,
1586
+ candidate_account_keys_json TEXT NOT NULL DEFAULT '[]'
1587
+ CHECK (json_valid(candidate_account_keys_json)),
1454
1588
  secret_generation INTEGER NOT NULL CHECK (secret_generation >= 0),
1455
1589
  nonce TEXT NOT NULL,
1456
1590
  ciphertext TEXT NOT NULL,
@@ -1467,17 +1601,24 @@ export class SqliteStore {
1467
1601
  updated_at INTEGER NOT NULL
1468
1602
  ) STRICT, WITHOUT ROWID;
1469
1603
  CREATE UNIQUE INDEX ilink_one_pending_offer_idx
1470
- ON ilink_login_offers(source_open_kfid, source_external_userid)
1604
+ ON ilink_login_offers(
1605
+ source_channel, source_account_id, source_peer_id
1606
+ )
1471
1607
  WHERE status IN ('waiting', 'scanned');
1472
1608
 
1473
1609
  CREATE TABLE ilink_enrollment_audit (
1474
1610
  offer_id TEXT PRIMARY KEY,
1611
+ initiator_kind TEXT NOT NULL
1612
+ CHECK (initiator_kind IN ('local_operator', 'remote_adapter')),
1613
+ source_channel TEXT NOT NULL,
1475
1614
  source_message_key TEXT NOT NULL,
1476
- source_open_kfid TEXT NOT NULL,
1477
- source_external_userid TEXT NOT NULL,
1615
+ source_account_id TEXT NOT NULL,
1616
+ source_peer_id TEXT NOT NULL,
1478
1617
  account_key TEXT NOT NULL DEFAULT '',
1479
- result TEXT NOT NULL
1480
- CHECK (result IN ('confirmed', 'expired', 'failed', 'cancelled')),
1618
+ result TEXT NOT NULL CHECK (result IN (
1619
+ 'confirmed', 'expired', 'failed', 'cancelled',
1620
+ 'already_connected', 'verification_required'
1621
+ )),
1481
1622
  offered_at INTEGER NOT NULL,
1482
1623
  completed_at INTEGER NOT NULL
1483
1624
  ) STRICT, WITHOUT ROWID;
@@ -1 +1 @@
1
- export const KINTIO_VERSION = '0.6.2';
1
+ export const KINTIO_VERSION = '0.7.1';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kin-tio/cli",
3
- "version": "0.6.2",
3
+ "version": "0.7.1",
4
4
  "license": "Apache-2.0",
5
5
  "author": "XIE YU",
6
6
  "packageManager": "pnpm@10.34.5",