@chipschat/opencode 1.0.4 → 1.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.
package/README.md CHANGED
@@ -11,6 +11,7 @@ server (`opencode serve`, 2.0).
11
11
  - A copy of an agent a person asks for in another room (Fork) is a copy of its session in its own
12
12
  room, in the same folder: it remembers what the agent did there, and from then on the two
13
13
  remember apart.
14
+ - Restart on the harness's page restarts the OpenCode server the bridge runs (see [Restart](#restart)).
14
15
 
15
16
  Apache-2.0. Not published: a local repository until Chips's SDK is.
16
17
 
@@ -27,16 +28,18 @@ Apache-2.0. Not published: a local repository until Chips's SDK is.
27
28
  | Files in the reply | each `MEDIA:<path>` line of its own in the answer, taken out of the words and sent from the session's folder (the agent's own), as OpenCode says it (each turn is told how); a file outside it is refused, and the reply says so |
28
29
  | Cost and tokens | each model call's `session.step.ended`: OpenCode's own cost, as a step's share |
29
30
  | The context gauge | the last call's tokens against the model's window from `GET /api/model` |
30
- | An approval card | `permission.asked` → `POST …/permission/{id}/reply`: `always` when a person said always, else `once`; `reject` with their reason for a no. One answered in OpenCode's own apps is settled in Chips (ADR 0079) |
31
+ | An approval card | `permission.asked` → `POST …/permission/{id}/reply`: `always` when a person said always, else `once`, and `once` for a command, which Chips remembers by how each part starts (Chips ADR 0239); `reject` with their reason for a no. One answered in OpenCode's own apps is settled in Chips (ADR 0079) |
31
32
  | A question | a `form.created` whose fields are choices or yes-or-no, at most four → `POST …/form/{id}/reply` |
32
33
  | Stop | `POST …/interrupt`, and the words OpenCode hasn't taken yet taken back |
33
34
  | A steer, a follow-up, an interrupt | a prompt with `delivery: "steer"`; with `"queue"`; interrupt, then a prompt |
34
35
  | Fork (Chips ADR 0087): a copy of an agent in another room | `POST /api/session/{id}/fork` of the agent's session in its room, which copies what it remembers; then `PATCH /api/session/{id}` names the copy for its room and marks it as the copy's. The copy is registered after it (*Nova · fork* for Nova), `forkedFrom` the original, for the room asked, and works in the original's folder. An ask heard again registers the same copy; an agent that never spoke in its room is copied as a new one with nothing to remember |
35
36
  | Not right? on an agent's reply (Chips ADR 0200), or a 👎 from an older device | a run of the agent who said it, in its session for the room, after its turns there: a prompt with its reply, what wasn't right, and to answer again in its place; the goal is *Looking again:* and the note's first line. A 👍, or any other reaction, is nothing |
36
37
  | Trouble on the harness | `GET /api/info` every 30 s |
38
+ | Restart (Chips ADR 0094) | the bridge's own `opencode serve` stopped and started again on the same port and password; then `GET /api/info`: fine, or why it isn't back |
37
39
 
38
40
  OpenCode asks before anything its config and the agent don't allow (its rules fall back to "ask"),
39
- so its permission asks reach Chips as cards. An `always` there is saved by OpenCode as well.
41
+ so its permission asks reach Chips as cards. An `always` there is saved by OpenCode as well; a
42
+ command never gets one, so the next command comes back to Chips, whose rule answers it.
40
43
 
41
44
  When the event stream drops, OpenCode doesn't replay what was missed. When it's back, the bridge
42
45
  reads each turn's session: which words were taken, whether it is still working, and how its last
@@ -52,6 +55,30 @@ Not yet:
52
55
  bridge that joins with a code asks for `forks` as well.
53
56
  - More than one OpenCode server per bridge.
54
57
 
58
+ ## Restart
59
+
60
+ Where the bridge runs OpenCode's server itself (no `url` in the config), a person can restart it
61
+ from the harness's page in Chips (Chips ADR 0094). The bridge stops the server and starts it again
62
+ on the same port and password, so its sessions, which OpenCode keeps on disk, go on where they
63
+ were. A server it took up from before, left running by a bridge that was stopped hard, is stopped
64
+ by the process it says it is. The bridge says nothing more until the server answers again, then
65
+ says it is fine. If the server doesn't come back, it says why, in OpenCode's own words where it has
66
+ them, ends the runs that were under way, and Chips offers Restart again. A restart stops whatever
67
+ OpenCode was doing, as Chips's sheet says.
68
+
69
+ A server you run yourself (`url` in the config) isn't the bridge's to restart. The bridge doesn't
70
+ ask for `restarts` when it joins, and doesn't hear the ask. A harness joined one way and later
71
+ configured the other keeps what it was granted when it joined:
72
+ - Joined running its own server, then given a `url`: Chips still draws Restart, and answers it
73
+ with "Chips can't reach <its name> to restart it". Turn off *Be restarted from Chips* on the
74
+ harness's page.
75
+ - Joined with a `url`, then running its own server: the bridge hears restarts, but Chips draws no
76
+ Restart until you turn on *Be restarted from Chips* on the harness's page.
77
+
78
+ A link carries what the person chose on it, not what the bridge asks for. The gallery's card for
79
+ OpenCode doesn't grant `restarts` yet, so a bridge joined from it gets Restart once you turn it on
80
+ on its page.
81
+
55
82
  ## From Chips's gallery
56
83
 
57
84
  The card's line is `chips connect opencode '<link>'` (Chips ADR 0218). It downloads this bridge as the npm package
@@ -1,6 +1,6 @@
1
1
  # Third-party notices
2
2
 
3
- `dist/main.js` in @chipschat/opencode 1.0.4 carries these packages, bundled in. Each one's
3
+ `dist/main.js` in @chipschat/opencode 1.0.6 carries these packages, bundled in. Each one's
4
4
  licence follows.
5
5
 
6
6
  ## @chipschat/harness 0.0.0 (Apache-2.0)
package/dist/main.js CHANGED
@@ -25698,6 +25698,9 @@ async function failureOf(answer) {
25698
25698
 
25699
25699
  // src/bridge.ts
25700
25700
  var WANTS = ["agents", "new_agents", "forks"];
25701
+ function wantsFor(ownServer2) {
25702
+ return ownServer2 ? [...WANTS, "restarts"] : WANTS;
25703
+ }
25701
25704
  function freeFolder(root, name, taken) {
25702
25705
  const slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
25703
25706
  for (let count = 1; ; count += 1) {
@@ -25708,6 +25711,7 @@ function freeFolder(root, name, taken) {
25708
25711
  var ids = createIdGenerator();
25709
25712
  var QUIET_MS = 1500;
25710
25713
  var INTERRUPTING = "interrupting";
25714
+ var TROUBLE_CHARS = 200;
25711
25715
  var LOOKS_KEPT = 500;
25712
25716
  var REPLY_CHARS = 1500;
25713
25717
  var KINDS = {
@@ -25760,6 +25764,12 @@ var Bridge = class {
25760
25764
  health;
25761
25765
  listening = new AbortController();
25762
25766
  connections = 0;
25767
+ /** Opens the event stream again now, rather than once its wait is over. */
25768
+ wake;
25769
+ /** A person's Restart under way: nothing is said of how it is meanwhile (Chips ADR 0094). */
25770
+ restarting;
25771
+ /** Why OpenCode's server isn't there, where the bridge knows more than that it doesn't answer. */
25772
+ why;
25763
25773
  /** Register the agents, start hearing OpenCode and what is said in Chips, and say how it is. */
25764
25774
  async start() {
25765
25775
  const { chips: chips2 } = this.options;
@@ -25790,6 +25800,37 @@ var Bridge = class {
25790
25800
  if (this.health !== void 0) clearInterval(this.health);
25791
25801
  this.listening.abort();
25792
25802
  }
25803
+ /**
25804
+ * A person's Restart (Chips ADR 0094): the server stopped and started again, then how it is,
25805
+ * fine or why not. Asked again meanwhile, it is the same restart. Nothing where the bridge
25806
+ * doesn't run the server, which never says it hears restarts.
25807
+ */
25808
+ restart() {
25809
+ this.restarting ??= (async () => {
25810
+ try {
25811
+ await this.restartServer();
25812
+ } finally {
25813
+ this.restarting = void 0;
25814
+ }
25815
+ await this.sayHowItIs();
25816
+ })();
25817
+ return this.restarting;
25818
+ }
25819
+ async restartServer() {
25820
+ const again2 = this.options.restartServer;
25821
+ if (again2 === void 0) return;
25822
+ this.log("a person asked for a restart");
25823
+ try {
25824
+ await again2();
25825
+ this.why = void 0;
25826
+ this.wake?.();
25827
+ } catch (error62) {
25828
+ const why = messageOf(error62).replace(/\s+/g, " ").trim();
25829
+ this.why = why.length <= TROUBLE_CHARS ? why : `${why.slice(0, TROUBLE_CHARS - 1)}\u2026`;
25830
+ this.log(`OpenCode's server isn't back: ${why}`);
25831
+ for (const turn of this.turns.values()) turn.end({ outcome: "failed", why: "OpenCode didn't come back after a restart" });
25832
+ }
25833
+ }
25793
25834
  /** The Main agent, the first registered, once started: who says the harness's first words. */
25794
25835
  get mainAgentId() {
25795
25836
  return this.agents[0]?.agentId;
@@ -25823,11 +25864,17 @@ var Bridge = class {
25823
25864
  const there = known.find((agent) => agent.agentId !== main2.agentId && agent.spaces.includes(home3));
25824
25865
  await chips2.agents.attach(main2.agentId, { spaceId: home3, replacing: there?.agentId ?? chips2.harnessId }).catch((error62) => this.log(`${main2.name} stays out of its home: ${String(error62)}`));
25825
25866
  }
25826
- /** Trouble while the server doesn't answer, and fine once it does (ADR 0083). */
25867
+ /**
25868
+ * Trouble while the server doesn't answer, and fine once it does (ADR 0083). Nothing while a
25869
+ * restart is under way: its end says (ADR 0094).
25870
+ */
25827
25871
  async sayHowItIs() {
25872
+ if (this.restarting !== void 0) return;
25828
25873
  const up = await this.options.opencode.healthy();
25874
+ if (this.restarting !== void 0) return;
25875
+ if (up) this.why = void 0;
25829
25876
  const chips2 = this.options.chips;
25830
- await (up ? chips2.reportOk() : chips2.reportTrouble("OpenCode isn't answering")).catch(() => void 0);
25877
+ await (up ? chips2.reportOk() : chips2.reportTrouble(this.why ?? "OpenCode isn't answering")).catch(() => void 0);
25831
25878
  }
25832
25879
  /** OpenCode's event stream, opened again whenever it drops, a little later each time it fails. */
25833
25880
  async listen() {
@@ -25841,8 +25888,15 @@ var Bridge = class {
25841
25888
  if (this.listening.signal.aborted) return;
25842
25889
  this.log(`OpenCode's events: ${messageOf(error62)}`);
25843
25890
  }
25844
- await new Promise((resolve5) => setTimeout(resolve5, wait2));
25845
- wait2 = Math.min(wait2 * 2, 15e3);
25891
+ const woken = await new Promise((resolve5) => {
25892
+ const timer = setTimeout(() => resolve5(false), wait2);
25893
+ this.wake = () => {
25894
+ clearTimeout(timer);
25895
+ resolve5(true);
25896
+ };
25897
+ });
25898
+ this.wake = void 0;
25899
+ wait2 = woken ? first : Math.min(wait2 * 2, 15e3);
25846
25900
  }
25847
25901
  }
25848
25902
  /**
@@ -26043,7 +26097,7 @@ var Bridge = class {
26043
26097
  return;
26044
26098
  }
26045
26099
  let end2 = () => void 0;
26046
- const ended = new Promise((resolve5) => {
26100
+ const ended2 = new Promise((resolve5) => {
26047
26101
  end2 = resolve5;
26048
26102
  });
26049
26103
  const turn = {
@@ -26076,7 +26130,7 @@ var Bridge = class {
26076
26130
  const words4 = `${steer.from?.display_name ?? "The person"} adds: ${steer.text}`;
26077
26131
  void this.steered(turn, words4, steer.mode);
26078
26132
  });
26079
- const last = await ended;
26133
+ const last = await ended2;
26080
26134
  await turn.chain;
26081
26135
  if (this.turns.get(sessionId) === turn) this.turns.delete(sessionId);
26082
26136
  if (last.outcome === "succeeded") await this.finish(turn);
@@ -26442,7 +26496,12 @@ var Bridge = class {
26442
26496
  if (decision === void 0 || pending === void 0 || pending.answered) return;
26443
26497
  pending.answered = true;
26444
26498
  if (decision.allowed && toolCall !== void 0) turn.allowed.set(toolCall, approvalId);
26445
- await this.options.opencode.replyPermission(askingSession, requestId, decisionOf(decision), decision.allowed ? void 0 : decision.reason).catch((error62) => this.log(`OpenCode didn't take the answer: ${messageOf(error62)}`));
26499
+ await this.options.opencode.replyPermission(
26500
+ askingSession,
26501
+ requestId,
26502
+ decisionOf(decision, card.action.kind === "shell.exec"),
26503
+ decision.allowed ? void 0 : decision.reason
26504
+ ).catch((error62) => this.log(`OpenCode didn't take the answer: ${messageOf(error62)}`));
26446
26505
  }
26447
26506
  /** A permission answered in OpenCode's own apps: settled in Chips, once, so the card cools (ADR 0079). */
26448
26507
  async settled(turn, data) {
@@ -26580,9 +26639,9 @@ var Bridge = class {
26580
26639
  if (retryable) void this.sayHowItIs();
26581
26640
  }
26582
26641
  };
26583
- function decisionOf(decision) {
26642
+ function decisionOf(decision, command = false) {
26584
26643
  if (!decision.allowed) return "reject";
26585
- return decision.scope === "always" ? "always" : "once";
26644
+ return decision.scope === "always" && !command ? "always" : "once";
26586
26645
  }
26587
26646
  function questionOf(field, title2) {
26588
26647
  const prompt = (field.description ?? field.title ?? title2).slice(0, 500);
@@ -26797,6 +26856,7 @@ var ServerError = class extends Error {
26797
26856
  missing;
26798
26857
  name = "ServerError";
26799
26858
  };
26859
+ var sleep = (ms) => new Promise((resolve5) => setTimeout(resolve5, ms));
26800
26860
  var onPath = (command, env) => command.includes("/") ? existsSync5(command) : (env["PATH"] ?? "").split(delimiter2).some((folder2) => {
26801
26861
  if (folder2 === "") return false;
26802
26862
  try {
@@ -26817,18 +26877,22 @@ async function freePort() {
26817
26877
  });
26818
26878
  return await tryPort(4096) ?? await tryPort(0) ?? 4096;
26819
26879
  }
26820
- async function answers2(url2, password) {
26880
+ async function infoOf(url2, password) {
26821
26881
  try {
26822
26882
  const answer = await fetch(`${url2}/api/info`, {
26823
26883
  headers: { authorization: `Basic ${Buffer.from(`opencode:${password}`).toString("base64")}` },
26824
26884
  signal: AbortSignal.timeout(2e3)
26825
26885
  });
26826
- if (answer.ok) return "ok";
26886
+ if (answer.ok) return await answer.json().catch(() => ({})) ?? {};
26827
26887
  return answer.status === 401 ? "refused" : "none";
26828
26888
  } catch {
26829
26889
  return "none";
26830
26890
  }
26831
26891
  }
26892
+ async function answers2(url2, password) {
26893
+ const info = await infoOf(url2, password);
26894
+ return typeof info === "string" ? info : "ok";
26895
+ }
26832
26896
  function keptIn(path) {
26833
26897
  if (!existsSync5(path)) return void 0;
26834
26898
  try {
@@ -26838,27 +26902,41 @@ function keptIn(path) {
26838
26902
  return void 0;
26839
26903
  }
26840
26904
  }
26905
+ function alive(pid) {
26906
+ try {
26907
+ process.kill(pid, 0);
26908
+ return true;
26909
+ } catch (error62) {
26910
+ return error62.code === "EPERM";
26911
+ }
26912
+ }
26913
+ async function ended(pid, ms) {
26914
+ for (const signal of ["SIGTERM", "SIGKILL"]) {
26915
+ try {
26916
+ process.kill(pid, signal);
26917
+ } catch {
26918
+ return;
26919
+ }
26920
+ for (const by = Date.now() + (signal === "SIGTERM" ? ms : 2e3); alive(pid) && Date.now() < by; ) await sleep(50);
26921
+ if (!alive(pid)) return;
26922
+ }
26923
+ }
26841
26924
  async function ownServer(options) {
26842
26925
  const env = options.env ?? process.env;
26843
26926
  const command = options.command ?? "opencode";
26844
26927
  const log2 = options.log ?? (() => void 0);
26845
26928
  const path = join7(options.folder, "server.json");
26846
26929
  let kept = keptIn(path);
26930
+ let before = false;
26847
26931
  if (kept !== void 0) {
26848
- const url3 = `http://127.0.0.1:${kept.port}`;
26849
- const now = await answers2(url3, kept.password);
26850
- if (now === "ok") {
26851
- log2(`OpenCode's server from before is still answering at ${url3}`);
26852
- return { url: url3, password: kept.password, stop: () => void 0 };
26853
- }
26932
+ const now = await answers2(`http://127.0.0.1:${kept.port}`, kept.password);
26933
+ before = now === "ok";
26854
26934
  if (now === "refused") kept = { port: await freePort(), password: kept.password };
26855
26935
  }
26856
- if (!onPath(command, env)) {
26936
+ if (!before && !onPath(command, env)) {
26857
26937
  throw new ServerError("OpenCode isn't installed on this machine: install it (opencode.ai), then run this again.", true);
26858
26938
  }
26859
26939
  kept ??= { port: await freePort(), password: randomBytes(24).toString("base64url") };
26860
- writeFileSync4(path, `${JSON.stringify(kept, null, 2)}
26861
- `, { mode: 384 });
26862
26940
  const { port, password } = kept;
26863
26941
  const url2 = `http://127.0.0.1:${port}`;
26864
26942
  let child;
@@ -26866,8 +26944,11 @@ async function ownServer(options) {
26866
26944
  let answered = false;
26867
26945
  let failed;
26868
26946
  let wait2 = 2e3;
26947
+ let again2;
26869
26948
  const said = [];
26870
26949
  const start = () => {
26950
+ writeFileSync4(path, `${JSON.stringify({ port, password }, null, 2)}
26951
+ `, { mode: 384 });
26871
26952
  const started = spawn(command, ["serve", "--hostname", "127.0.0.1", "--port", String(port)], {
26872
26953
  cwd: options.cwd,
26873
26954
  env: { ...env, OPENCODE_PASSWORD: password },
@@ -26889,9 +26970,10 @@ async function ownServer(options) {
26889
26970
  return;
26890
26971
  }
26891
26972
  log2(`OpenCode's server stopped (${signal ?? `exit ${code}`}): starting it again in ${wait2 / 1e3} s`);
26892
- setTimeout(() => {
26893
- if (!stopping) start();
26894
- }, wait2).unref();
26973
+ again2 = setTimeout(() => {
26974
+ if (!stopping && child === void 0) start();
26975
+ }, wait2);
26976
+ again2.unref();
26895
26977
  wait2 = Math.min(wait2 * 2, 6e4);
26896
26978
  });
26897
26979
  started.on("error", (error62) => {
@@ -26900,25 +26982,68 @@ async function ownServer(options) {
26900
26982
  });
26901
26983
  child = started;
26902
26984
  };
26903
- start();
26904
- const by = Date.now() + (options.waitMs ?? 3e4);
26905
- while (await answers2(url2, password) !== "ok") {
26906
- if (failed !== void 0 || Date.now() > by) {
26907
- stopping = true;
26908
- child?.kill("SIGTERM");
26909
- const last = said.slice(-3).join(" ").trim();
26910
- throw new ServerError(`OpenCode's server didn't start at ${url2}${last === "" ? "" : `: ${last}`}`);
26985
+ const up = async (anew) => {
26986
+ stopping = false;
26987
+ answered = false;
26988
+ failed = void 0;
26989
+ said.length = 0;
26990
+ start();
26991
+ const by = Date.now() + (options.waitMs ?? 3e4);
26992
+ while (await answers2(url2, password) !== "ok") {
26993
+ if (failed !== void 0 || Date.now() > by) {
26994
+ stopping = true;
26995
+ child?.kill("SIGTERM");
26996
+ const last = said.slice(-3).join(" ").trim();
26997
+ throw new ServerError(`OpenCode's server didn't start${anew ? " again" : ""} at ${url2}${last === "" ? "" : `: ${last}`}`);
26998
+ }
26999
+ await sleep(250);
27000
+ }
27001
+ answered = true;
27002
+ wait2 = 2e3;
27003
+ };
27004
+ const down = async () => {
27005
+ stopping = true;
27006
+ clearTimeout(again2);
27007
+ const stopMs = options.stopMs ?? 1e4;
27008
+ const pid = child?.pid;
27009
+ if (pid !== void 0) return ended(pid, stopMs);
27010
+ const info = await infoOf(url2, password);
27011
+ if (typeof info === "string") return;
27012
+ if (typeof info["pid"] !== "number" || info["pid"] <= 0 || info["pid"] === process.pid) {
27013
+ throw new ServerError(
27014
+ `OpenCode's server at ${url2} was running before this bridge and doesn't say which process it is: stop it on this machine, then restart again.`
27015
+ );
26911
27016
  }
26912
- await new Promise((resolve5) => setTimeout(resolve5, 250));
27017
+ await ended(info["pid"], stopMs);
27018
+ };
27019
+ if (before) log2(`OpenCode's server from before is still answering at ${url2}`);
27020
+ else {
27021
+ await up(false);
27022
+ log2(`OpenCode's own server answering at ${url2}`);
26913
27023
  }
26914
- answered = true;
26915
- log2(`OpenCode's own server answering at ${url2}`);
27024
+ let restarting;
27025
+ const restart = async () => {
27026
+ if (!onPath(command, env)) {
27027
+ throw new ServerError("OpenCode isn't installed on this machine any more: install it (opencode.ai), then restart again.", true);
27028
+ }
27029
+ log2(`restarting OpenCode's server at ${url2}`);
27030
+ await down();
27031
+ await up(true);
27032
+ log2(`OpenCode's server is answering again at ${url2}`);
27033
+ };
26916
27034
  return {
26917
27035
  url: url2,
26918
27036
  password,
26919
27037
  stop: () => {
26920
27038
  stopping = true;
27039
+ clearTimeout(again2);
26921
27040
  child?.kill("SIGTERM");
27041
+ },
27042
+ restart: () => {
27043
+ restarting ??= restart().finally(() => {
27044
+ restarting = void 0;
27045
+ });
27046
+ return restarting;
26922
27047
  }
26923
27048
  };
26924
27049
  }
@@ -27068,20 +27193,24 @@ var joined = await join3({
27068
27193
  url: process.env["CHIPS_URL"],
27069
27194
  name: config2.name,
27070
27195
  software: "chips-opencode 1.0.0",
27071
- wants: WANTS,
27196
+ wants: wantsFor(server !== void 0),
27072
27197
  onCode: (code) => log(`Allow this harness in Chips: ${code}`)
27073
27198
  }).catch(refused2);
27074
27199
  keepIfAsked(LABEL, joined.url, [config2.passwordEnv], log, () => server?.stop());
27075
27200
  var state = existsSync7(statePath) ? JSON.parse(readFileSync7(statePath, "utf8")) : { sessions: {} };
27201
+ var bridge;
27076
27202
  var chips = await connect({
27077
27203
  url: joined.url,
27078
27204
  token: joined.token,
27079
27205
  client: { name: "chips-opencode", version: "1.0.0" },
27080
- store: fileStore(join9(folder, "chips-state.json"))
27206
+ store: fileStore(join9(folder, "chips-state.json")),
27207
+ // A person's Restart (Chips ADR 0094), heard only where the bridge runs OpenCode's server: one a
27208
+ // person runs themselves isn't the bridge's to restart, so this connection doesn't say it hears them.
27209
+ ...server === void 0 ? {} : { onRestart: () => bridge?.restart() }
27081
27210
  });
27082
27211
  var git = await checkGit();
27083
27212
  if (!git.ok) log(`rooms' projects are off: ${git.why}`);
27084
- var bridge = new Bridge({
27213
+ bridge = new Bridge({
27085
27214
  chips,
27086
27215
  opencode,
27087
27216
  projects: git.ok,
@@ -27091,6 +27220,7 @@ var bridge = new Bridge({
27091
27220
  saveState: (next) => writeFileSync5(statePath, `${JSON.stringify(next, null, 2)}
27092
27221
  `, { mode: 384 }),
27093
27222
  log,
27223
+ restartServer: server?.restart,
27094
27224
  ...config2.directory === void 0 ? { folders: config2.folders } : {}
27095
27225
  });
27096
27226
  await bridge.start();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chipschat/opencode",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "OpenCode as a Chips harness, on its V2 server (opencode serve): each OpenCode agent a Chips agent, each room an OpenCode session.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",