@chipschat/opencode 1.0.7 → 1.0.10
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 +11 -9
- package/dist/THIRD-PARTY-NOTICES.md +1 -1
- package/dist/main.js +215 -31
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ server (`opencode serve`, 2.0).
|
|
|
21
21
|
A bridge that joins with a code asks for all of these. One that joined from Chips's gallery does
|
|
22
22
|
what its card grants: a link carries what the person chose on it, not what the bridge asks for.
|
|
23
23
|
|
|
24
|
-
Apache-2.0
|
|
24
|
+
Apache-2.0, published on npm as `@chipschat/opencode`.
|
|
25
25
|
|
|
26
26
|
## What Chips shows
|
|
27
27
|
|
|
@@ -144,22 +144,22 @@ gets Restart once you turn it on on its page.
|
|
|
144
144
|
|
|
145
145
|
## From Chips's gallery
|
|
146
146
|
|
|
147
|
-
The card's line is `chips connect opencode
|
|
147
|
+
The card's line is `CHIPS_JOIN_LINK='<link>' chips connect opencode` (Chips ADR 0218). It downloads this bridge as the npm package
|
|
148
148
|
`@chipschat/opencode`, the newest 1.x, checks it against the registry's checksum, and runs it on the Node
|
|
149
149
|
that Chips carries, so the machine needs no Node or npm of its own. The bridge checks OpenCode,
|
|
150
150
|
joins, and keeps itself running from login. `chips disconnect opencode` stops it. On another computer,
|
|
151
|
-
`curl -fsSL https://chipschat.ai/install | sh -s -- connect opencode
|
|
151
|
+
`curl -fsSL https://chipschat.ai/install | CHIPS_JOIN_LINK='<link>' sh -s -- connect opencode` installs Chips there
|
|
152
152
|
first. The steps below run the bridge from this checkout instead, to work on it.
|
|
153
153
|
|
|
154
154
|
## Run it
|
|
155
155
|
|
|
156
156
|
1. Install OpenCode (opencode.ai), and `npm install` once in this repository.
|
|
157
157
|
|
|
158
|
-
2. Join Chips once, with a link from Chips's gallery (
|
|
158
|
+
2. Join Chips once, with a link from Chips's gallery (Agents → Connect a harness → OpenCode), whose card gives
|
|
159
159
|
this line with the link in it:
|
|
160
160
|
|
|
161
161
|
```
|
|
162
|
-
cd
|
|
162
|
+
cd chips-opencode && CHIPS_JOIN_LINK='<link>' npm run keep
|
|
163
163
|
```
|
|
164
164
|
|
|
165
165
|
The bridge runs OpenCode's server itself (`opencode serve` on `127.0.0.1`, 4096 when it is
|
|
@@ -213,7 +213,9 @@ the key it kept. The scratch Chips, the link and the run come from Chips's `tool
|
|
|
213
213
|
## Release
|
|
214
214
|
|
|
215
215
|
`npm run release` builds `dist/main.js`, one file with the SDK and everything else bundled in, and
|
|
216
|
-
`dist/THIRD-PARTY-NOTICES.md` beside it; runs the tests; and packs `chipschat-opencode-<version>.tgz`.
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
216
|
+
`dist/THIRD-PARTY-NOTICES.md` beside it; runs the tests; and packs `chipschat-opencode-<version>.tgz`. To publish,
|
|
217
|
+
change `version` in package.json (`npm version <x> --no-git-tag-version`), commit, and push to main:
|
|
218
|
+
`.github/workflows/publish.yml` runs the tests and publishes that version from GitHub with npm's
|
|
219
|
+
trusted publishing, and skips a version npm already has. A version's major is the Chips protocol it
|
|
220
|
+
speaks, and Chips's card names that major (Chips ADR 0218): a fix is a minor or a patch, and reaches
|
|
221
|
+
every card of its major the day it is published.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Third-party notices
|
|
2
2
|
|
|
3
|
-
`dist/main.js` in @chipschat/opencode 1.0.
|
|
3
|
+
`dist/main.js` in @chipschat/opencode 1.0.10 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
|
@@ -8,7 +8,7 @@ var __export = (target, all) => {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
// src/main.ts
|
|
11
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as
|
|
11
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "node:fs";
|
|
12
12
|
import { homedir as homedir7 } from "node:os";
|
|
13
13
|
import { dirname as dirname5, join as join10, resolve as resolve4 } from "node:path";
|
|
14
14
|
|
|
@@ -19943,8 +19943,9 @@ var policyRule = external_exports.object({
|
|
|
19943
19943
|
external: external_exports.boolean().optional(),
|
|
19944
19944
|
irreversible: external_exports.boolean().optional(),
|
|
19945
19945
|
money_usd_over: external_exports.number().nonnegative().optional(),
|
|
19946
|
-
risk: external_exports.array(riskClass).max(8).optional()
|
|
19947
|
-
|
|
19946
|
+
risk: external_exports.array(riskClass).max(8).optional(),
|
|
19947
|
+
starts_with: external_exports.array(external_exports.string().min(1).max(400)).min(1).max(8).optional().describe("For an action that carries a command (ADR 0239): the words a part of it starts with. A command goes ahead on such rules only when every part starts with one that allows it.")
|
|
19948
|
+
}).optional().describe("Extra conditions on the action's flags, and for a command, how its parts start."),
|
|
19948
19949
|
decision: external_exports.enum(["allow", "ask", "deny"]).describe("What to do when the rule matches."),
|
|
19949
19950
|
approvers: external_exports.array(approverRef).max(16).optional().describe("Who may answer, when the decision is `ask`."),
|
|
19950
19951
|
reason: external_exports.string().max(400).optional().describe("Shown to the harness, and recorded in the audit log."),
|
|
@@ -20463,9 +20464,17 @@ var roomIdle = external_exports.object({
|
|
|
20463
20464
|
var roomNote = external_exports.object({
|
|
20464
20465
|
members: external_exports.array(roomMember).max(500).describe("The room's people, then its agents, each by name, anybody removed left out."),
|
|
20465
20466
|
notes: external_exports.record(external_exports.string().max(120), external_exports.string().max(64e3)).describe("The note for each who takes the turn (`for`), by their id: who is here, with handles, and that only those @-mentioned act. A harness puts it at the top of the turn."),
|
|
20467
|
+
rules: external_exports.record(external_exports.string().max(120), external_exports.string().max(4e3)).optional().describe("How the space works, for each who takes the turn, by their id: the words its note ends with (ADR 0250), that everyone here reads what it writes and, beside other agents, what an @ does and when to answer NO_REPLY. They stay the same while the space's agents do, so a harness whose session keeps its earlier turns may say them once, and the note without them after."),
|
|
20466
20468
|
working: external_exports.array(roomWork).max(500).optional().describe("The room's agents with a turn going as Chips sends this one (ADR 0104), the takers' own included. A turn nothing has been heard from for ten minutes is not counted."),
|
|
20467
20469
|
idle: external_exports.array(roomIdle).max(500).optional().describe("The room's other agents that have had a turn here: how long since the last one ended (ADR 0104).")
|
|
20468
20470
|
});
|
|
20471
|
+
function noteWithoutRules(room, taker) {
|
|
20472
|
+
const note = room?.notes[taker];
|
|
20473
|
+
const rules = room?.rules?.[taker];
|
|
20474
|
+
if (note === void 0 || rules === void 0 || !note.endsWith(rules))
|
|
20475
|
+
return note;
|
|
20476
|
+
return note.slice(0, note.length - rules.length).trimEnd();
|
|
20477
|
+
}
|
|
20469
20478
|
var MONTHS = [
|
|
20470
20479
|
"January",
|
|
20471
20480
|
"February",
|
|
@@ -20485,8 +20494,8 @@ var MONTHS = [
|
|
|
20485
20494
|
var CONTEXT_CEILING = { lines: 200, chars: 32e3 };
|
|
20486
20495
|
var CONTEXT_HEADING = "Said in this space since you last spoke:";
|
|
20487
20496
|
var contextBudget = external_exports.object({
|
|
20488
|
-
lines: external_exports.int().nonnegative().optional().describe("The most lines to be handed,
|
|
20489
|
-
chars: external_exports.int().nonnegative().optional().describe("The most characters those lines may run to, as `Name: words` one per line,
|
|
20497
|
+
lines: external_exports.int().nonnegative().optional().describe("The most lines to be handed, 40 when absent. Chips hands over 200 at most, and none when this is 0."),
|
|
20498
|
+
chars: external_exports.int().nonnegative().optional().describe("The most characters those lines may run to, as `Name: words` one per line, 16,000 when absent. Chips hands over 32,000 at most.")
|
|
20490
20499
|
}).describe("How much of the room a harness is handed with a message for it (ADR 0088). Its own to say: a budget promises nothing to anybody else.");
|
|
20491
20500
|
var contextLine = external_exports.object({
|
|
20492
20501
|
seq: external_exports.int().positive().describe("Where in the log it happened."),
|
|
@@ -20698,12 +20707,14 @@ var contextAnswer = {
|
|
|
20698
20707
|
message: external_exports.string().min(1).max(300).optional().describe("What went wrong, for the person.")
|
|
20699
20708
|
}).optional().describe("An edit not made. The copy beside it is the file as it is now.")
|
|
20700
20709
|
};
|
|
20710
|
+
var readNote = external_exports.string().min(1).max(300).describe("When the agent reads these, in the harness's words: `Hermes reads these when a conversation starts.` Absent: at the start of every turn.");
|
|
20701
20711
|
function uniqueNames(files) {
|
|
20702
20712
|
return new Set(files.map((file3) => file3.name)).size === files.length;
|
|
20703
20713
|
}
|
|
20704
20714
|
var agentContextPut = external_exports.object({
|
|
20705
20715
|
files: external_exports.array(contextFile).max(CONTEXT_FILES_SHOWN).refine(uniqueNames, { message: "Each file is named once." }).describe("The agent's files, in the order its page shows them: eight at most."),
|
|
20706
20716
|
not_shown: external_exports.array(notShownFile).max(CONTEXT_NOT_SHOWN_MAX).optional(),
|
|
20717
|
+
read_note: readNote.optional(),
|
|
20707
20718
|
...contextAnswer
|
|
20708
20719
|
}).describe("`PUT /v1/agents/:id/context`: a harness's copy of one of its agents' files.");
|
|
20709
20720
|
var harnessContextPut = external_exports.object({
|
|
@@ -20736,6 +20747,7 @@ var agentContextView = external_exports.object({
|
|
|
20736
20747
|
harness_id: id("harness"),
|
|
20737
20748
|
files: external_exports.array(contextFile),
|
|
20738
20749
|
not_shown: external_exports.array(notShownFile),
|
|
20750
|
+
read_note: readNote.optional(),
|
|
20739
20751
|
read_at: isoTimestamp.describe("When the harness last sent its copy."),
|
|
20740
20752
|
away: external_exports.boolean().describe("No socket of the harness hears edits now: read, but not saved.")
|
|
20741
20753
|
}).describe("`GET /v1/agents/:id/context`, for the harness's owner.");
|
|
@@ -20863,7 +20875,7 @@ var messageNew = defineEvent({
|
|
|
20863
20875
|
summary: "Someone said something, with any attachments as signed URLs the harness can fetch.",
|
|
20864
20876
|
requiresRun: false,
|
|
20865
20877
|
payload: deliveredMessage.extend({
|
|
20866
|
-
context: roomContext.optional().describe("What was said in the room since this harness last spoke there, or since it joined when it has not, up to this message and without it (ADR 0088). From when the turn that said its last words began, so what was said while it worked is here, and its own words are not (ADR 0105). Worked out from the log, so a replay hands over the same lines. As many as the budget the harness gave in its `hello`:
|
|
20878
|
+
context: roomContext.optional().describe("What was said in the room since this harness last spoke there, or since it joined when it has not, up to this message and without it (ADR 0088). From when the turn that said its last words began, so what was said while it worked is here, and its own words are not (ADR 0105). Worked out from the log, so a replay hands over the same lines. As many as the budget the harness gave in its `hello`: 40 lines and 16,000 characters when it gave none, and none when it asked for 0. When older lines were left out, the taker's room note says so."),
|
|
20867
20879
|
room: roomNote.optional().describe("Who is in the room, with the handle that reaches each, and a note for each who takes the turn saying so and what an @ does (ADR 0092). A harness puts `notes[<taker>]` at the top of that turn. Absent on a message in no room."),
|
|
20868
20880
|
exchange: deliveredExchange.optional().describe("How the agents' talk in the room stands, once it is worth a word (ADR 0097): from eight messages between agents since a person last acted or an agent last made something new. A harness puts `note` at the top of the turn, as Chips's, ahead of the room's note. Absent below that, and worked out from the log, so a replay hands over the same facts."),
|
|
20869
20881
|
handed: external_exports.literal(true).optional().describe("Chips held this message when it was said and hands it over now a person has acted (ADR 0097): the newest of an agent's held messages, once; or, with `lead`, hands it to the room's lead. Its seq and id are the hand-over's, its `message_id` the message's own."),
|
|
@@ -20913,6 +20925,7 @@ var approvalResolved = defineEvent({
|
|
|
20913
20925
|
approval_id: id("approval"),
|
|
20914
20926
|
decision: external_exports.enum(["approved", "edited", "denied", "expired"]).describe("`expired` means nobody answered and the fallback applied."),
|
|
20915
20927
|
scope: external_exports.enum(["once", "session", "always"]).default("once").describe("How far the answer reaches. `always` becomes a policy rule."),
|
|
20928
|
+
everywhere: external_exports.boolean().optional().describe("With `always`: the rule is the harness's in every space, not only this one (ADR 0239). An owner or an admin only."),
|
|
20916
20929
|
updated_input: external_exports.unknown().optional().describe("The edited action input, when the decision is `edited`."),
|
|
20917
20930
|
reason: external_exports.string().max(4e3).optional().describe("Why. The harness gets to read this."),
|
|
20918
20931
|
resolved_by: external_exports.string().min(1).max(120).describe("The participant who answered, or `system` on expiry \u2014 a harness when it settles its own ask, answered in its own tools. Filled in by Chips from the credential that sent it; a value the sender writes is replaced."),
|
|
@@ -21177,6 +21190,10 @@ function namesInWords(names) {
|
|
|
21177
21190
|
return `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
|
|
21178
21191
|
}
|
|
21179
21192
|
function renderProjectNote(facts, options) {
|
|
21193
|
+
return `${renderProjectFacts(facts)}
|
|
21194
|
+
${renderProjectAsks(facts, options)}`;
|
|
21195
|
+
}
|
|
21196
|
+
function renderProjectFacts(facts) {
|
|
21180
21197
|
const files = `${facts.files} file${facts.files === 1 ? "" : "s"}`;
|
|
21181
21198
|
const lines2 = [
|
|
21182
21199
|
`The space's project is a git repository. Your copy is ${facts.folder} (${files}), ${facts.trouble === void 0 ? "brought up to date just now" : "not brought up to date"}.`
|
|
@@ -21194,9 +21211,9 @@ function renderProjectNote(facts, options) {
|
|
|
21194
21211
|
for (const key of facts.keys) {
|
|
21195
21212
|
lines2.push(`${key.path} holds what looks like ${key.what}, so your last change wasn't sent: take it out and read it from outside the project.`);
|
|
21196
21213
|
}
|
|
21197
|
-
return
|
|
21214
|
+
return lines2.join("\n");
|
|
21198
21215
|
}
|
|
21199
|
-
function
|
|
21216
|
+
function renderProjectAsks(facts, options) {
|
|
21200
21217
|
const lines2 = [
|
|
21201
21218
|
"Work on the project in that folder and leave git to Chips: when your turn ends, what you changed is committed under your name and sent to everyone here. Keep scratch work outside it.",
|
|
21202
21219
|
// Sent with the answer too, a project's file became a room file of its own, with versions of
|
|
@@ -21217,7 +21234,7 @@ function asks(facts, options) {
|
|
|
21217
21234
|
if (facts.pages === true) {
|
|
21218
21235
|
lines2.push("A page opens in Chips as one document: name every file it needs in its tags and imports (<link>, <script src>, <img src>, import), since it can't fetch files while it runs.");
|
|
21219
21236
|
}
|
|
21220
|
-
return lines2;
|
|
21237
|
+
return lines2.join("\n");
|
|
21221
21238
|
}
|
|
21222
21239
|
|
|
21223
21240
|
// node_modules/@chipschat/protocol/dist/src/events/harness-to-chips.js
|
|
@@ -21869,7 +21886,7 @@ var membershipRemoved = defineEvent({
|
|
|
21869
21886
|
payload: external_exports.object({
|
|
21870
21887
|
participant_id: external_exports.string().min(1).max(120).describe("Who left."),
|
|
21871
21888
|
removed_by: external_exports.string().min(1).max(120).optional().describe("Who removed them, if not themselves."),
|
|
21872
|
-
reason: external_exports.enum([...RELEASE_REASONS, "person_removed"]).optional().describe("For an agent (ADR 0087): why it left, as `agent.released` says. For a person, `person_removed`: they were removed from the workspace (ADR 0160). Written by Chips; absent, a person took them out or they left."),
|
|
21889
|
+
reason: external_exports.enum([...RELEASE_REASONS, "person_removed", "person_left"]).optional().describe("For an agent (ADR 0087): why it left, as `agent.released` says. For a person, `person_removed`: they were removed from the workspace (ADR 0160); `person_left`: they deleted their account and left it (ADR 0243). Written by Chips; absent, a person took them out or they left the room."),
|
|
21873
21890
|
moved_to: id("space").optional().describe("For an agent moved away: the room it went to, so this room can say so.")
|
|
21874
21891
|
})
|
|
21875
21892
|
});
|
|
@@ -22178,14 +22195,14 @@ var personRemoved = defineEvent({
|
|
|
22178
22195
|
type: "person.removed",
|
|
22179
22196
|
direction: "workspace",
|
|
22180
22197
|
sender: "chips",
|
|
22181
|
-
summary: "A person was removed from the workspace: signed out everywhere, out of every room, and the harnesses they owned handed
|
|
22198
|
+
summary: "A person was removed from the workspace, or deleted their account: signed out everywhere, out of every room, and the harnesses they owned handed on. What they said stays, under their name.",
|
|
22182
22199
|
requiresRun: false,
|
|
22183
22200
|
payload: external_exports.object({
|
|
22184
22201
|
participant_id: id("person").describe("Who was removed."),
|
|
22185
22202
|
display_name: external_exports.string().min(1).max(120).describe("What they were called when they were removed, so the audit needs no lookup."),
|
|
22186
|
-
removed_by: id("person").describe("The owner or admin who removed them."),
|
|
22203
|
+
removed_by: id("person").describe("The owner or admin who removed them, or they themselves when they deleted their account (ADR 0243)."),
|
|
22187
22204
|
spaces: external_exports.array(id("space")).max(1e3).optional().describe("The rooms they were taken out of, each with its own `membership.removed`."),
|
|
22188
|
-
harnesses: external_exports.array(id("harness")).max(1e3).optional().describe("The harnesses they owned, handed to `removed_by
|
|
22205
|
+
harnesses: external_exports.array(id("harness")).max(1e3).optional().describe("The harnesses they owned, each handed on by `harness.transferred`: to `removed_by`, or, for an account deleted, to an owner.")
|
|
22189
22206
|
})
|
|
22190
22207
|
});
|
|
22191
22208
|
var joinWho = {
|
|
@@ -22401,6 +22418,89 @@ var EVENT_TYPES = EVENTS.map((event) => event.type);
|
|
|
22401
22418
|
var byType = new Map(EVENTS.map((event) => [event.type, event]));
|
|
22402
22419
|
var schemas = new Map(EVENTS.map((event) => [event.type, eventSchema(event)]));
|
|
22403
22420
|
|
|
22421
|
+
// node_modules/@chipschat/protocol/dist/src/names.js
|
|
22422
|
+
var LOOK_LIKE = {
|
|
22423
|
+
A: "\u0410\u0391",
|
|
22424
|
+
// Cyrillic А, Greek Α
|
|
22425
|
+
B: "\u0412\u0392",
|
|
22426
|
+
// Cyrillic В, Greek Β
|
|
22427
|
+
C: "\u0421",
|
|
22428
|
+
// Cyrillic С
|
|
22429
|
+
E: "\u0415\u0395",
|
|
22430
|
+
// Cyrillic Е, Greek Ε
|
|
22431
|
+
H: "\u041D\u0397",
|
|
22432
|
+
// Cyrillic Н, Greek Η
|
|
22433
|
+
I: "\u0406\u04C0\u0399",
|
|
22434
|
+
// Cyrillic І and Ӏ, Greek Ι
|
|
22435
|
+
J: "\u0408",
|
|
22436
|
+
// Cyrillic Ј
|
|
22437
|
+
K: "\u041A\u039A",
|
|
22438
|
+
// Cyrillic К, Greek Κ
|
|
22439
|
+
M: "\u041C\u039C",
|
|
22440
|
+
// Cyrillic М, Greek Μ
|
|
22441
|
+
N: "\u039D",
|
|
22442
|
+
// Greek Ν
|
|
22443
|
+
O: "\u041E\u039F",
|
|
22444
|
+
// Cyrillic О, Greek Ο
|
|
22445
|
+
P: "\u0420\u03A1",
|
|
22446
|
+
// Cyrillic Р, Greek Ρ
|
|
22447
|
+
Q: "\u051A",
|
|
22448
|
+
// Cyrillic Ԛ
|
|
22449
|
+
S: "\u0405",
|
|
22450
|
+
// Cyrillic Ѕ
|
|
22451
|
+
T: "\u0422\u03A4",
|
|
22452
|
+
// Cyrillic Т, Greek Τ
|
|
22453
|
+
W: "\u051C",
|
|
22454
|
+
// Cyrillic Ԝ
|
|
22455
|
+
X: "\u0425\u03A7",
|
|
22456
|
+
// Cyrillic Х, Greek Χ
|
|
22457
|
+
Y: "\u0423\u04AE\u03A5",
|
|
22458
|
+
// Cyrillic У and Ү, Greek Υ
|
|
22459
|
+
Z: "\u0396",
|
|
22460
|
+
// Greek Ζ
|
|
22461
|
+
a: "\u0430\u03B1",
|
|
22462
|
+
// Cyrillic а, Greek α
|
|
22463
|
+
c: "\u0441",
|
|
22464
|
+
// Cyrillic с
|
|
22465
|
+
d: "\u0501",
|
|
22466
|
+
// Cyrillic ԁ
|
|
22467
|
+
e: "\u0435",
|
|
22468
|
+
// Cyrillic е
|
|
22469
|
+
h: "\u04BB",
|
|
22470
|
+
// Cyrillic һ
|
|
22471
|
+
i: "\u0456\u03B9\u0131",
|
|
22472
|
+
// Cyrillic і, Greek ι, Latin dotless ı
|
|
22473
|
+
j: "\u0458",
|
|
22474
|
+
// Cyrillic ј
|
|
22475
|
+
k: "\u03BA",
|
|
22476
|
+
// Greek κ
|
|
22477
|
+
l: "\u04CF",
|
|
22478
|
+
// Cyrillic ӏ
|
|
22479
|
+
o: "\u043E\u03BF",
|
|
22480
|
+
// Cyrillic о, Greek ο
|
|
22481
|
+
p: "\u0440\u03C1",
|
|
22482
|
+
// Cyrillic р, Greek ρ
|
|
22483
|
+
q: "\u051B",
|
|
22484
|
+
// Cyrillic ԛ
|
|
22485
|
+
s: "\u0455",
|
|
22486
|
+
// Cyrillic ѕ
|
|
22487
|
+
u: "\u03C5",
|
|
22488
|
+
// Greek υ
|
|
22489
|
+
v: "\u03BD",
|
|
22490
|
+
// Greek ν
|
|
22491
|
+
w: "\u051D",
|
|
22492
|
+
// Cyrillic ԝ
|
|
22493
|
+
x: "\u0445\u03C7",
|
|
22494
|
+
// Cyrillic х, Greek χ
|
|
22495
|
+
y: "\u0443\u04AF"
|
|
22496
|
+
// Cyrillic у and ү
|
|
22497
|
+
};
|
|
22498
|
+
var LATIN_OF = new Map(Object.entries(LOOK_LIKE).flatMap(([latin, others]) => [...others].map((other) => [other, latin])));
|
|
22499
|
+
var HIDDEN = "\\p{Cc}\\p{Cf}\\u034f\\u115f\\u1160\\u3164\\uffa0\\u2800";
|
|
22500
|
+
var UNSEEN = new RegExp(`[${HIDDEN}\\ufe00-\\ufe0f\\u{e0100}-\\u{e01ef}]`, "gu");
|
|
22501
|
+
var HIDDEN_ONE = new RegExp(`[${HIDDEN}]`, "u");
|
|
22502
|
+
var ALPHABETS = [new RegExp("\\p{Script=Latin}", "u"), new RegExp("\\p{Script=Cyrillic}", "u"), new RegExp("\\p{Script=Greek}", "u")];
|
|
22503
|
+
|
|
22404
22504
|
// node_modules/@chipschat/protocol/dist/src/first-names.js
|
|
22405
22505
|
var AGENT_FIRST_NAMES = [
|
|
22406
22506
|
"Abel Aldo Amir Amos Anders Ansel Anton Ari Arlo Arne Aron Arvid Asher Avi Axel Aziz",
|
|
@@ -22448,7 +22548,7 @@ var clientHelloFrame = external_exports.object({
|
|
|
22448
22548
|
})).max(64).optional().describe("Not read by Chips: a connection is its token's, and the server's `hello` names the token's harness and its agents (ADR 0087). Absent for a person's client."),
|
|
22449
22549
|
since: external_exports.int().nonnegative().optional().describe("Not read by Chips: a stream resumes from `subscribe`'s `since`."),
|
|
22450
22550
|
accepts: external_exports.array(external_exports.string().min(1).max(40)).max(32).optional().describe("Server frames beyond the base set this client understands, by the names in `SERVER_EXTENSIONS`. A name the server does not know is ignored rather than refused, so a client may name a frame a newer server sends and still be answered by an older one (ADR 0211, Phase 3). A client that says `events` is sent its replay in pages rather than one frame per event; one that says `presence` is sent an agent's state as it changes (ADR 0083); a harness's socket that says `restart` is sent `restart` when a person asks it to restart (ADR 0094); an owner's or admin's that says `join` is sent each join request as it changes (ADR 0122); a harness's that says `context` is sent `context.edit` when a person changes one of its context files (ADR 0134); a harness's that says `models` is sent `model.change` when a person changes an agent's model or effort (ADR 0140); a harness's that says `heartbeats` is sent `heartbeat.change` when a person switches an agent's heartbeat or changes how often it beats (ADR 0143); a harness's that says `context_windows` is sent `context_window.compact` and `context_window.change` when a person asks an agent to compact now or changes the limit it compacts itself at (ADR 0146); a person's that says `folders` is sent `folder.open` when they ask, from another of their devices, for a room's folder to be opened in Finder (ADR 0225), and `folder.run` when they ask for its project to be run or stopped there (ADR 0226); a person's that says `folder_runs` is sent `folder.run_state` when their Mac says how a run of theirs stands (ADR 0226); one that says nothing is sent what it was always sent, since an unknown frame is dropped in silence."),
|
|
22451
|
-
context: contextBudget.optional().describe("How much of a room a harness is handed with each message for it (ADR 0088):
|
|
22551
|
+
context: contextBudget.optional().describe("How much of a room a harness is handed with each message for it (ADR 0088): 40 lines and 16,000 characters when absent, 200 and 32,000 at most. For this connection. A person's client is handed none.")
|
|
22452
22552
|
}).describe("The first frame a client sends.");
|
|
22453
22553
|
var subscribeFrame = external_exports.object({
|
|
22454
22554
|
frame: external_exports.literal("subscribe"),
|
|
@@ -25010,8 +25110,19 @@ async function redeem(url2, secret, options, send, machine) {
|
|
|
25010
25110
|
throw await refused(answered);
|
|
25011
25111
|
return await answered.json();
|
|
25012
25112
|
}
|
|
25113
|
+
function reaching(send, url2) {
|
|
25114
|
+
return async (input2, init) => {
|
|
25115
|
+
try {
|
|
25116
|
+
return await send(input2, init);
|
|
25117
|
+
} catch (error62) {
|
|
25118
|
+
if (error62 instanceof Error && error62.name === "AbortError")
|
|
25119
|
+
throw error62;
|
|
25120
|
+
throw new JoinError("unreachable", `Can't reach Chips at ${url2} from this computer. It has to be on the same network as Chips, on its tailnet, or Chips has to be at an address this computer can reach.`);
|
|
25121
|
+
}
|
|
25122
|
+
};
|
|
25123
|
+
}
|
|
25013
25124
|
function askOrRedeem(server2, options) {
|
|
25014
|
-
const send = options.fetch ?? fetch;
|
|
25125
|
+
const send = reaching(options.fetch ?? fetch, server2.url);
|
|
25015
25126
|
const machine = options.machine ?? hostname3();
|
|
25016
25127
|
return server2.secret === void 0 ? ask(server2.url, options, send, machine) : redeem(server2.url, server2.secret, options, send, machine);
|
|
25017
25128
|
}
|
|
@@ -25177,14 +25288,31 @@ async function sendAnswer(answer, options) {
|
|
|
25177
25288
|
}
|
|
25178
25289
|
|
|
25179
25290
|
// node_modules/@chipschat/harness/dist/prompt.js
|
|
25291
|
+
function takerOf(message, options) {
|
|
25292
|
+
return options.taker ?? (message.for?.length === 1 ? message.for[0] : void 0);
|
|
25293
|
+
}
|
|
25294
|
+
function othersBeside(message, taker) {
|
|
25295
|
+
return (message.room?.members ?? []).some((member) => member.kind === "agent" && member.participant_id !== taker);
|
|
25296
|
+
}
|
|
25297
|
+
function rulesFor(message, options = {}) {
|
|
25298
|
+
const taker = takerOf(message, options);
|
|
25299
|
+
const { project } = options;
|
|
25300
|
+
const rules = message.room?.rules ?? {};
|
|
25301
|
+
return [
|
|
25302
|
+
taker === void 0 ? void 0 : rules[taker],
|
|
25303
|
+
project === void 0 ? void 0 : renderProjectAsks(project, { othersInRoom: othersBeside(message, taker) }),
|
|
25304
|
+
options.sendsFiles === true ? sendingFiles({ project: project !== void 0 }) : void 0
|
|
25305
|
+
].filter((part) => part !== void 0 && part !== "").join("\n\n");
|
|
25306
|
+
}
|
|
25180
25307
|
function promptFor(message, options = {}) {
|
|
25181
|
-
const taker =
|
|
25308
|
+
const taker = takerOf(message, options);
|
|
25309
|
+
const held = options.rulesHeld === true;
|
|
25182
25310
|
const notes = message.room?.notes ?? {};
|
|
25183
|
-
const note = taker === void 0 ? void 0 : notes[taker];
|
|
25311
|
+
const note = taker === void 0 ? void 0 : held ? noteWithoutRules(message.room, taker) : notes[taker];
|
|
25184
25312
|
const who = options.sayWho === false ? "" : `${withRole(message.from.display_name, message.from)}: `;
|
|
25185
25313
|
const files = (message.attachments ?? []).map(renderAttachment);
|
|
25186
25314
|
const words4 = [`${who}${message.text}`, ...files].join("\n");
|
|
25187
|
-
const sending = options.sendsFiles === true ? sendingFiles({ project: options.project !== void 0 }) : void 0;
|
|
25315
|
+
const sending = options.sendsFiles === true && !held ? sendingFiles({ project: options.project !== void 0 }) : void 0;
|
|
25188
25316
|
const replying = renderReplyingTo(message.replying_to, {
|
|
25189
25317
|
sender: {
|
|
25190
25318
|
id: message.from.participant_id,
|
|
@@ -25196,8 +25324,7 @@ function promptFor(message, options = {}) {
|
|
|
25196
25324
|
at: message.ts,
|
|
25197
25325
|
zone: options.zone
|
|
25198
25326
|
});
|
|
25199
|
-
const
|
|
25200
|
-
const project = options.project === void 0 ? void 0 : renderProjectNote(options.project, { othersInRoom: others });
|
|
25327
|
+
const project = options.project === void 0 ? void 0 : held ? renderProjectFacts(options.project) : renderProjectNote(options.project, { othersInRoom: othersBeside(message, taker) });
|
|
25201
25328
|
return [
|
|
25202
25329
|
message.exchange?.note,
|
|
25203
25330
|
note,
|
|
@@ -25209,6 +25336,24 @@ function promptFor(message, options = {}) {
|
|
|
25209
25336
|
].filter((part) => part !== void 0 && part !== "").join("\n\n");
|
|
25210
25337
|
}
|
|
25211
25338
|
|
|
25339
|
+
// node_modules/@chipschat/harness/dist/rules.js
|
|
25340
|
+
var SessionRules = class {
|
|
25341
|
+
held = /* @__PURE__ */ new Map();
|
|
25342
|
+
/** Whether `session` holds these rules: told them word for word, or there are none to hold. */
|
|
25343
|
+
holds(session, rules) {
|
|
25344
|
+
return rules === "" || this.held.get(session) === rules;
|
|
25345
|
+
}
|
|
25346
|
+
/** That `session` was told these rules: its turn that said them was given to it. */
|
|
25347
|
+
told(session, rules) {
|
|
25348
|
+
if (rules !== "")
|
|
25349
|
+
this.held.set(session, rules);
|
|
25350
|
+
}
|
|
25351
|
+
/** That `session` no longer holds what it was told: it compacted, or it is gone. */
|
|
25352
|
+
forget(session) {
|
|
25353
|
+
this.held.delete(session);
|
|
25354
|
+
}
|
|
25355
|
+
};
|
|
25356
|
+
|
|
25212
25357
|
// node_modules/@chipschat/harness/dist/service.js
|
|
25213
25358
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
25214
25359
|
import { accessSync, chmodSync as chmodSync2, constants, existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -25384,7 +25529,10 @@ function nodeOnPath(env = process.env) {
|
|
|
25384
25529
|
}
|
|
25385
25530
|
var pause = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
25386
25531
|
var LOAD_TRIES = 40;
|
|
25387
|
-
var runOf = (options) => options.run ?? ((command) =>
|
|
25532
|
+
var runOf = (options) => options.run ?? ((command) => execFileSync2(command[0], command.slice(1), {
|
|
25533
|
+
encoding: "utf8",
|
|
25534
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
25535
|
+
}));
|
|
25388
25536
|
var ours = (path) => !existsSync3(path) || readFileSync3(path, "utf8").includes(MARK);
|
|
25389
25537
|
function runOne(command, run2, retryMs) {
|
|
25390
25538
|
const tries = command[0] === "launchctl" && command[1] === "bootstrap" ? LOAD_TRIES : 1;
|
|
@@ -25407,6 +25555,24 @@ function runAll(commands, options) {
|
|
|
25407
25555
|
runOne(command, run2, options.retryMs ?? 250);
|
|
25408
25556
|
}
|
|
25409
25557
|
var uidOf = (options) => options.uid ?? process.getuid?.() ?? 0;
|
|
25558
|
+
function lingers(options) {
|
|
25559
|
+
const run2 = runOf(options);
|
|
25560
|
+
const uid = String(uidOf(options));
|
|
25561
|
+
try {
|
|
25562
|
+
const on = run2(["loginctl", "show-user", uid, "--property=Linger", "--value"]);
|
|
25563
|
+
if (typeof on === "string" && on.trim() === "yes")
|
|
25564
|
+
return true;
|
|
25565
|
+
} catch {
|
|
25566
|
+
}
|
|
25567
|
+
try {
|
|
25568
|
+
run2(["loginctl", "enable-linger", uid]);
|
|
25569
|
+
return true;
|
|
25570
|
+
} catch {
|
|
25571
|
+
return false;
|
|
25572
|
+
}
|
|
25573
|
+
}
|
|
25574
|
+
var sayOf = (options) => options.say ?? ((line) => void process.stderr.write(`${line}
|
|
25575
|
+
`));
|
|
25410
25576
|
function keepRunning(spec, options = {}) {
|
|
25411
25577
|
if (machineOf(options.machine).env["CHIPS_KEEP"] === "off")
|
|
25412
25578
|
return { state: "off" };
|
|
@@ -25421,6 +25587,9 @@ function keepRunning(spec, options = {}) {
|
|
|
25421
25587
|
writeFileSync2(file3.path, file3.contents, { mode: 384 });
|
|
25422
25588
|
chmodSync2(file3.path, 384);
|
|
25423
25589
|
runAll(startCommands(file3.kind, spec.label, file3.path, uidOf(options)), options);
|
|
25590
|
+
if (file3.kind === "systemd" && !lingers(options)) {
|
|
25591
|
+
sayOf(options)("It stops when you log out. To keep it running on a machine nobody logs in to, run this once: sudo loginctl enable-linger $USER");
|
|
25592
|
+
}
|
|
25424
25593
|
return { state: "kept", path: file3.path, logFile: file3.logFile };
|
|
25425
25594
|
}
|
|
25426
25595
|
function stopKeeping(label2, options = {}) {
|
|
@@ -26184,6 +26353,8 @@ var Bridge = class {
|
|
|
26184
26353
|
rooms = /* @__PURE__ */ new Map();
|
|
26185
26354
|
/** The turn going in each OpenCode session. */
|
|
26186
26355
|
turns = /* @__PURE__ */ new Map();
|
|
26356
|
+
/** The room's rules each session holds (Chips ADR 0250): said once, and again after it compacts. */
|
|
26357
|
+
rules = new SessionRules();
|
|
26187
26358
|
/**
|
|
26188
26359
|
* What a session said while it was being given a turn's words, before the turn was known: OpenCode
|
|
26189
26360
|
* may say it took them before it answers that it has them. Heard by the turn once it is.
|
|
@@ -26728,6 +26899,7 @@ var Bridge = class {
|
|
|
26728
26899
|
* refused after all, in OpenCode's words.
|
|
26729
26900
|
*/
|
|
26730
26901
|
compactionHeard(sessionId, event) {
|
|
26902
|
+
if (event.type === "session.compaction.started") this.rules.forget(sessionId);
|
|
26731
26903
|
const who = this.whoseSession(sessionId);
|
|
26732
26904
|
const kept = who === void 0 ? void 0 : this.options.state.windows?.[who];
|
|
26733
26905
|
if (who === void 0 || kept === void 0) return;
|
|
@@ -26885,16 +27057,18 @@ var Bridge = class {
|
|
|
26885
27057
|
const speaker = speakerId === void 0 ? this.options.chips : this.options.chips.as(speakerId);
|
|
26886
27058
|
const run2 = await speaker.runs.start({ spaceId, goal: goal ?? goalOf(message, agent.config.name) });
|
|
26887
27059
|
const readied = await this.projectFor(room, speakerId ?? agent.agentId);
|
|
26888
|
-
const
|
|
27060
|
+
const asked = {
|
|
26889
27061
|
...speakerId === void 0 ? {} : { taker: speakerId },
|
|
26890
27062
|
sayWho: saysWho !== true,
|
|
26891
27063
|
sendsFiles: true,
|
|
26892
27064
|
project: readied?.facts
|
|
26893
|
-
}
|
|
27065
|
+
};
|
|
27066
|
+
const rules = rulesFor(message, asked);
|
|
27067
|
+
const said = (session) => promptFor(message, { ...asked, rulesHeld: this.rules.holds(session, rules) });
|
|
26894
27068
|
let sessionId;
|
|
26895
27069
|
let given;
|
|
26896
27070
|
try {
|
|
26897
|
-
({ sessionId, given } = await this.give(room,
|
|
27071
|
+
({ sessionId, given } = await this.give(room, said, rules));
|
|
26898
27072
|
} catch (error62) {
|
|
26899
27073
|
await this.failed(run2, `${agent.config.name} couldn't reach OpenCode: ${messageOf(error62)}`, true);
|
|
26900
27074
|
return;
|
|
@@ -26947,7 +27121,7 @@ var Bridge = class {
|
|
|
26947
27121
|
await this.turnHeld(turn);
|
|
26948
27122
|
}
|
|
26949
27123
|
/** The words into the room's session: the one it had, or a new one when OpenCode no longer has it. */
|
|
26950
|
-
async give({ id: id2, who, agent, spaceId },
|
|
27124
|
+
async give({ id: id2, who, agent, spaceId }, said, rules) {
|
|
26951
27125
|
const { opencode: opencode2, state: state2, saveState } = this.options;
|
|
26952
27126
|
let kept = state2.sessions[id2];
|
|
26953
27127
|
if (kept === void 0 && this.copying.size > 0) {
|
|
@@ -26957,7 +27131,7 @@ var Bridge = class {
|
|
|
26957
27131
|
if (kept !== void 0) {
|
|
26958
27132
|
try {
|
|
26959
27133
|
await this.modelled(kept, { who, agent });
|
|
26960
|
-
return { sessionId: kept, given: await this.prompted(kept,
|
|
27134
|
+
return { sessionId: kept, given: await this.prompted(kept, said(kept), rules) };
|
|
26961
27135
|
} catch (error62) {
|
|
26962
27136
|
if (!(error62 instanceof OpenCodeError && error62.tag === "SessionNotFoundError")) throw error62;
|
|
26963
27137
|
this.log(`${agent.config.name}'s session for ${spaceId} is gone from OpenCode: a new one`);
|
|
@@ -26973,7 +27147,7 @@ var Bridge = class {
|
|
|
26973
27147
|
});
|
|
26974
27148
|
state2.sessions[id2] = made;
|
|
26975
27149
|
saveState(state2);
|
|
26976
|
-
return { sessionId: made, given: await this.prompted(made,
|
|
27150
|
+
return { sessionId: made, given: await this.prompted(made, said(made), rules) };
|
|
26977
27151
|
}
|
|
26978
27152
|
/**
|
|
26979
27153
|
* The folder an agent's new sessions work in, its own: made the first time, named after it, and
|
|
@@ -27043,12 +27217,18 @@ var Bridge = class {
|
|
|
27043
27217
|
return known.find((each) => each.agentId === who)?.name ?? "agent";
|
|
27044
27218
|
}
|
|
27045
27219
|
/** A turn's first words into a session, what it says meanwhile held for the turn. */
|
|
27046
|
-
|
|
27220
|
+
/**
|
|
27221
|
+
* A turn's words given to a session, which is told the room's rules they carry as they are
|
|
27222
|
+
* given, so a compaction heard during the turn forgets them (Chips ADR 0250).
|
|
27223
|
+
*/
|
|
27224
|
+
async prompted(sessionId, text2, rules) {
|
|
27047
27225
|
this.early.set(sessionId, []);
|
|
27226
|
+
this.rules.told(sessionId, rules);
|
|
27048
27227
|
try {
|
|
27049
27228
|
return await this.options.opencode.prompt(sessionId, text2);
|
|
27050
27229
|
} catch (error62) {
|
|
27051
27230
|
this.early.delete(sessionId);
|
|
27231
|
+
this.rules.forget(sessionId);
|
|
27052
27232
|
throw error62;
|
|
27053
27233
|
}
|
|
27054
27234
|
}
|
|
@@ -27966,6 +28146,10 @@ async function checkOpenCode(config3, client) {
|
|
|
27966
28146
|
}
|
|
27967
28147
|
}
|
|
27968
28148
|
|
|
28149
|
+
// src/version.ts
|
|
28150
|
+
import { readFileSync as readFileSync8 } from "node:fs";
|
|
28151
|
+
var VERSION = JSON.parse(readFileSync8(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
28152
|
+
|
|
27969
28153
|
// src/main.ts
|
|
27970
28154
|
var home2 = (path) => path.startsWith("~/") ? join10(homedir7(), path.slice(2)) : path;
|
|
27971
28155
|
var link = process.env["CHIPS_JOIN_LINK"] || void 0;
|
|
@@ -28033,17 +28217,17 @@ var joined = await join3({
|
|
|
28033
28217
|
link,
|
|
28034
28218
|
url: process.env["CHIPS_URL"],
|
|
28035
28219
|
name: config2.name,
|
|
28036
|
-
software:
|
|
28220
|
+
software: `chips-opencode ${VERSION}`,
|
|
28037
28221
|
wants: wantsFor(server !== void 0),
|
|
28038
28222
|
onCode: (code) => log(`Allow this harness in Chips: ${code}`)
|
|
28039
28223
|
}).catch(refused2);
|
|
28040
28224
|
keepIfAsked(LABEL, joined.url, [config2.passwordEnv], log, () => server?.stop());
|
|
28041
|
-
var state = existsSync7(statePath) ? JSON.parse(
|
|
28225
|
+
var state = existsSync7(statePath) ? JSON.parse(readFileSync9(statePath, "utf8")) : { sessions: {} };
|
|
28042
28226
|
var bridge;
|
|
28043
28227
|
var chips = await connect({
|
|
28044
28228
|
url: joined.url,
|
|
28045
28229
|
token: joined.token,
|
|
28046
|
-
client: { name: "chips-opencode", version:
|
|
28230
|
+
client: { name: "chips-opencode", version: VERSION },
|
|
28047
28231
|
store: fileStore(join10(folder, "chips-state.json")),
|
|
28048
28232
|
// A person's Restart (Chips ADR 0094), heard only where the bridge runs OpenCode's server: one a
|
|
28049
28233
|
// person runs themselves isn't the bridge's to restart, so this connection doesn't say it hears them.
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chipschat/opencode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
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
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/arjungupta99/chips-opencode.git"
|
|
9
|
+
},
|
|
6
10
|
"type": "module",
|
|
7
11
|
"engines": {
|
|
8
12
|
"node": ">=22.19.0"
|