@dashup/connector 0.1.17 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +79 -14
  3. package/package.json +4 -4
package/README.md CHANGED
@@ -47,9 +47,9 @@ Only an explicit Queue reply starts a temporary official app-server helper to in
47
47
 
48
48
  Delivery requires that exact typed client-message ID in the original thread's native history database. A response receipt requires a later native assistant item in the same turn; it does not mean the agent finished. Receipts remain encrypted through the ordinary Dashup relay. History is read on request; the phone asks again while a thread is open and its session changes. Native approvals owned by the private-stdio process remain unsupported and require its original UI. Queue text never substitutes for a native approval.
49
49
 
50
- Each thread shows what its agent's own record says it last ran with. A Claude Code session shows the model of its newest reply and, where that model takes them, fast mode and ultracode; the choices and their names are the models the session's own Claude Code binary offers hosts in its initialize answer, or `claude`'s where the system does not name the binary. A Codex thread shows the model and reasoning effort of its newest turn and, where the model has the priority tier, fast mode, named from Codex's own `models_cache.json`. A setting an agent records only when it changes, such as ultracode or Codex's tier, is searched for however far back it lies. A saved thread carries these, without choices, only in its own published state, never in a project's snapshot.
50
+ Each thread shows what its agent's own record says it last ran with. A Claude Code session shows the model of its newest reply and, where that model takes them, fast mode and ultracode; the choices and their names are the models the session's own Claude Code binary offers hosts in its initialize answer, or `claude`'s where the system does not name the binary. A Codex thread shows the model and reasoning effort of its newest turn and, where the model has the priority tier, fast mode, named from Codex's own `models_cache.json`. An OpenCode session shows the model and agent OpenCode keeps on the session (`/api/session`), among the models and primary agents it lists for the folder. A setting an agent records only when it changes, such as ultracode or Codex's tier, is searched for however far back it lies. A saved thread carries these, without choices, only in its own published state, never in a project's snapshot.
51
51
 
52
- The phone changes a running session's settings: through the Claude plugin with Claude Code's own `/model`, `/fast` and `/effort ultracode` commands; in a Claude session the phone started, with control requests at the start of each turn; and in a Codex thread the connector holds, with the next `turn/start` it sends. An agent records a change when its next turn starts, so it shows from the next reply. A change expires with other commands after 60 seconds.
52
+ The phone changes a running session's settings: through the Claude plugin with Claude Code's own `/model`, `/fast` and `/effort ultracode` commands; in a Claude session the phone started, with control requests at the start of each turn; in a Codex thread the connector holds, with the next `turn/start` it sends; and in an OpenCode session, on the session itself, so the phone's messages go in what it runs with. An agent records a change when its next turn starts, so it shows from the next reply. A change expires with other commands after 60 seconds.
53
53
 
54
54
  Stop interrupts the running turn through each live interface: OpenCode's session abort, Codex `turn/interrupt`, the AHP client's turn cancellation, ACP `session/cancel`, and the Claude Code plugin's turn abort. It refuses when nothing is running and expires with other commands after 60 seconds, so a late stop cannot end a newer turn.
55
55
 
package/dist/index.js CHANGED
@@ -4048,7 +4048,7 @@ import * as ui from "@clack/prompts";
4048
4048
  import { z } from "zod";
4049
4049
 
4050
4050
  // package.json
4051
- var version = "0.1.17";
4051
+ var version = "0.1.18";
4052
4052
 
4053
4053
  // src/pair.ts
4054
4054
  var Invitation = z.object({
@@ -18287,7 +18287,14 @@ ${NO_PREVIEW}`;
18287
18287
  ...change2 ? { change: change2 } : {}
18288
18288
  };
18289
18289
  }
18290
- function promptContext(history) {
18290
+ function promptContext(history, session) {
18291
+ const current = session?.model;
18292
+ if (typeof session?.agent === "string" && typeof current?.providerID === "string" && typeof current.id === "string")
18293
+ return {
18294
+ agent: session.agent,
18295
+ model: { providerID: current.providerID, modelID: current.id },
18296
+ ...typeof current.variant === "string" ? { variant: current.variant } : {}
18297
+ };
18291
18298
  if (!Array.isArray(history)) throw new Error("OpenCode session context is unavailable");
18292
18299
  const user = [...history].reverse().find((entry) => entry?.info?.role === "user")?.info;
18293
18300
  if (!user) return {};
@@ -18301,6 +18308,26 @@ function promptContext(history) {
18301
18308
  ...variant === void 0 ? {} : { variant }
18302
18309
  };
18303
18310
  }
18311
+ function sessionOptions(models2, agents, session) {
18312
+ const current = session?.model;
18313
+ if (typeof current?.providerID !== "string" || typeof current.id !== "string") return [];
18314
+ const value2 = `${current.providerID}/${current.id}`;
18315
+ const agent = typeof session?.agent === "string" ? session.agent : void 0;
18316
+ const listed = (Array.isArray(models2) ? models2 : []).filter((model) => typeof model?.providerID === "string" && typeof model.id === "string").map((model) => ({
18317
+ value: `${model.providerID}/${model.id}`,
18318
+ name: String(model.name ?? model.id)
18319
+ }));
18320
+ const primary = (Array.isArray(agents) ? agents : []).filter(
18321
+ (item) => typeof item?.id === "string" && item.mode !== "subagent" && !item.hidden
18322
+ ).map((item) => ({ value: String(item.id), name: String(item.name ?? item.id) }));
18323
+ if (!listed.some((choice) => choice.value === value2)) listed.push({ value: value2, name: current.id });
18324
+ if (agent && !primary.some((choice) => choice.value === agent))
18325
+ primary.push({ value: agent, name: agent });
18326
+ return [
18327
+ { id: "model", name: "Model", value: value2, choices: listed },
18328
+ ...agent ? [{ id: "agent", name: "Agent", value: agent, choices: primary }] : []
18329
+ ];
18330
+ }
18304
18331
  var Server = class {
18305
18332
  constructor(endpoint, changed) {
18306
18333
  this.endpoint = endpoint;
@@ -18313,6 +18340,8 @@ var Server = class {
18313
18340
  notify;
18314
18341
  stopped = false;
18315
18342
  requests = /* @__PURE__ */ new Map();
18343
+ // The models and agents OpenCode lists for each folder, asked once while it answers.
18344
+ catalogs = /* @__PURE__ */ new Map();
18316
18345
  headers(directory2) {
18317
18346
  return {
18318
18347
  ...this.endpoint.authorization ? { Authorization: this.endpoint.authorization } : {},
@@ -18347,12 +18376,29 @@ var Server = class {
18347
18376
  this.requests.set(id, pending);
18348
18377
  return pending;
18349
18378
  }
18350
- // A message to one of this folder's sessions, in the agent and model it last used; a new
18379
+ catalog(cwd) {
18380
+ let known = this.catalogs.get(cwd);
18381
+ if (!known) {
18382
+ known = Promise.all([
18383
+ this.request("/api/model", cwd).then((listed) => listed?.data),
18384
+ this.request("/api/agent", cwd).then((listed) => listed?.data)
18385
+ ]).catch(() => {
18386
+ this.catalogs.delete(cwd);
18387
+ return [[], []];
18388
+ });
18389
+ this.catalogs.set(cwd, known);
18390
+ }
18391
+ return known;
18392
+ }
18393
+ // A message to one of this folder's sessions, in the agent and model it runs with; a new
18351
18394
  // session takes OpenCode's defaults.
18352
18395
  async prompt(id, cwd, text3) {
18353
- const existing = await this.request(`/session/${encodeURIComponent(id)}/message?limit=200`, cwd);
18396
+ const [existing, session] = await Promise.all([
18397
+ this.request(`/session/${encodeURIComponent(id)}/message?limit=200`, cwd),
18398
+ this.request(`/api/session/${encodeURIComponent(id)}`, cwd).catch(() => void 0)
18399
+ ]);
18354
18400
  await this.request(`/session/${encodeURIComponent(id)}/prompt_async`, cwd, {
18355
- ...promptContext(existing),
18401
+ ...promptContext(existing, session?.data),
18356
18402
  parts: [{ type: "text", text: text3 }]
18357
18403
  });
18358
18404
  this.changed();
@@ -18382,11 +18428,16 @@ var Server = class {
18382
18428
  state = Promise.all([
18383
18429
  this.request("/session/status", cwd),
18384
18430
  this.request("/permission", cwd).catch(() => []),
18385
- this.request("/question", cwd).catch(() => [])
18431
+ this.request("/question", cwd).catch(() => []),
18432
+ this.request(`/api/session?limit=${MAX_SESSIONS4}`, cwd).then(
18433
+ (listed) => Array.isArray(listed?.data) ? listed.data : [],
18434
+ () => []
18435
+ ),
18436
+ this.catalog(cwd)
18386
18437
  ]);
18387
18438
  states.set(cwd, state);
18388
18439
  }
18389
- const [history, [status2, permissions, questions]] = await Promise.all([
18440
+ const [history, [status2, permissions, questions, settings, [models2, agents]]] = await Promise.all([
18390
18441
  this.request(`/session/${encodeURIComponent(id)}/message?limit=200`, cwd),
18391
18442
  state
18392
18443
  ]);
@@ -18396,6 +18447,7 @@ var Server = class {
18396
18447
  const asks = Array.isArray(questions) ? questions.filter(
18397
18448
  (question) => question?.sessionID === id && typeof question.id === "string"
18398
18449
  ) : [];
18450
+ const current = settings.find((item) => item?.id === id);
18399
18451
  const approvals = pending.map((permission) => permissionApproval(permission, history, cwd));
18400
18452
  for (const question of asks)
18401
18453
  approvals.push(
@@ -18416,8 +18468,8 @@ var Server = class {
18416
18468
  approvals,
18417
18469
  stop: (requestId) => this.once(requestId, async () => {
18418
18470
  if (this.stopped) throw new Error("OpenCode is disconnected");
18419
- const current = await this.request("/session/status", cwd);
18420
- if (!["busy", "retry"].includes(current?.[id]?.type))
18471
+ const current2 = await this.request("/session/status", cwd);
18472
+ if (!["busy", "retry"].includes(current2?.[id]?.type))
18421
18473
  throw new Error("Nothing is running in this OpenCode session");
18422
18474
  await this.request(`/session/${encodeURIComponent(id)}/abort`, cwd, {});
18423
18475
  this.changed();
@@ -18434,19 +18486,32 @@ var Server = class {
18434
18486
  if (Array.isArray(existing) && existing.some((entry) => entry?.info?.id === messageID))
18435
18487
  return;
18436
18488
  await this.request(`/session/${encodeURIComponent(id)}/prompt_async`, cwd, {
18437
- ...promptContext(existing),
18489
+ ...promptContext(existing, current),
18438
18490
  messageID,
18439
18491
  parts: [{ type: "text", text: text3 }]
18440
18492
  });
18441
18493
  this.changed();
18442
18494
  }),
18495
+ options: () => sessionOptions(models2, agents, current),
18496
+ // OpenCode keeps the model, as `provider/model`, and the agent on the session.
18497
+ configure: (requestId, option2, value2) => this.once(requestId, async () => {
18498
+ if (this.stopped) throw new Error("OpenCode is disconnected");
18499
+ const chosen = String(value2);
18500
+ const split = chosen.indexOf("/");
18501
+ await this.request(
18502
+ `/api/session/${encodeURIComponent(id)}/${option2}`,
18503
+ cwd,
18504
+ option2 === "model" ? { model: { providerID: chosen.slice(0, split), id: chosen.slice(split + 1) } } : { agent: chosen }
18505
+ );
18506
+ this.changed();
18507
+ }),
18443
18508
  approve: approvals.some((approval2) => approval2.choices.length) ? (requestId, approvalId, reply) => this.once(requestId, async () => {
18444
18509
  if (this.stopped) throw new Error("OpenCode is disconnected");
18445
18510
  const choice = "choice" in reply ? reply.choice : void 0;
18446
18511
  if (approvalId.startsWith("question:")) {
18447
18512
  const requestID = approvalId.slice("question:".length);
18448
- const current = await this.request("/question", cwd);
18449
- if (!Array.isArray(current) || !current.some(
18513
+ const current2 = await this.request("/question", cwd);
18514
+ if (!Array.isArray(current2) || !current2.some(
18450
18515
  (value2) => value2?.id === requestID && value2.sessionID === id
18451
18516
  ))
18452
18517
  throw new Error("Question is no longer pending");
@@ -18456,8 +18521,8 @@ var Server = class {
18456
18521
  "answers" in reply ? { answers: reply.answers } : {}
18457
18522
  );
18458
18523
  } else {
18459
- const current = await this.request("/permission", cwd);
18460
- const permission = Array.isArray(current) ? current.find(
18524
+ const current2 = await this.request("/permission", cwd);
18525
+ const permission = Array.isArray(current2) ? current2.find(
18461
18526
  (value2) => value2?.id === approvalId && value2.sessionID === id
18462
18527
  ) : void 0;
18463
18528
  if (!PERMISSION_CHOICES.some((value2) => value2.id === choice) || !permission)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dashup/connector",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "dashup": "dist/index.js"
@@ -30,11 +30,11 @@
30
30
  "tsup": "^8.5.1",
31
31
  "tsx": "^4.22.4",
32
32
  "typescript": "^5.9.3",
33
+ "@dashup/models": "0.1.0",
33
34
  "@dashup/protocol": "0.1.0",
35
+ "@dashup/typescript-config": "0.0.0",
34
36
  "@parcae/lynx": "0.1.0",
35
- "@dashup/models": "0.1.0",
36
- "@parcae/sdk": "0.8.1",
37
- "@dashup/typescript-config": "0.0.0"
37
+ "@parcae/sdk": "0.8.1"
38
38
  },
39
39
  "scripts": {
40
40
  "dashup": "tsx src/index.ts",