@abaigram/abaikit 0.0.0-stage → 0.2.0

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 (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +15 -2
  3. package/abai/identity.md +1 -0
  4. package/abai/learnabai.md +546 -0
  5. package/abai/use.md +1 -0
  6. package/abaigram.yaml +208 -0
  7. package/dist/abaify.d.ts +68 -0
  8. package/dist/abaify.d.ts.map +1 -0
  9. package/dist/abaify.js +310 -0
  10. package/dist/abaify.js.map +1 -0
  11. package/dist/abaigram.d.ts +18 -0
  12. package/dist/abaigram.d.ts.map +1 -0
  13. package/dist/abaigram.js +153 -0
  14. package/dist/abaigram.js.map +1 -0
  15. package/dist/app.d.ts +19 -0
  16. package/dist/app.d.ts.map +1 -0
  17. package/dist/app.js +29 -0
  18. package/dist/app.js.map +1 -0
  19. package/dist/bin.d.ts +46 -0
  20. package/dist/bin.d.ts.map +1 -0
  21. package/dist/bin.js +274 -0
  22. package/dist/bin.js.map +1 -0
  23. package/dist/check.d.ts +119 -0
  24. package/dist/check.d.ts.map +1 -0
  25. package/dist/check.js +416 -0
  26. package/dist/check.js.map +1 -0
  27. package/dist/command.d.ts +41 -0
  28. package/dist/command.d.ts.map +1 -0
  29. package/dist/command.js +179 -0
  30. package/dist/command.js.map +1 -0
  31. package/dist/index.d.ts +5 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +5 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/io.d.ts +12 -0
  36. package/dist/io.d.ts.map +1 -0
  37. package/dist/io.js +20 -0
  38. package/dist/io.js.map +1 -0
  39. package/dist/learnabai.d.ts +60 -0
  40. package/dist/learnabai.d.ts.map +1 -0
  41. package/dist/learnabai.js +199 -0
  42. package/dist/learnabai.js.map +1 -0
  43. package/dist/new.d.ts +26 -0
  44. package/dist/new.d.ts.map +1 -0
  45. package/dist/new.js +87 -0
  46. package/dist/new.js.map +1 -0
  47. package/dist/repo.d.ts +99 -0
  48. package/dist/repo.d.ts.map +1 -0
  49. package/dist/repo.js +482 -0
  50. package/dist/repo.js.map +1 -0
  51. package/dist/run.d.ts +6 -0
  52. package/dist/run.d.ts.map +1 -0
  53. package/dist/run.js +47 -0
  54. package/dist/run.js.map +1 -0
  55. package/dist/subject.d.ts +15 -0
  56. package/dist/subject.d.ts.map +1 -0
  57. package/dist/subject.js +53 -0
  58. package/dist/subject.js.map +1 -0
  59. package/dist/template-ts.d.ts +21 -0
  60. package/dist/template-ts.d.ts.map +1 -0
  61. package/dist/template-ts.js +322 -0
  62. package/dist/template-ts.js.map +1 -0
  63. package/dist/version.d.ts +3 -0
  64. package/dist/version.d.ts.map +1 -0
  65. package/dist/version.js +3 -0
  66. package/dist/version.js.map +1 -0
  67. package/examples/hello/abai/identity.md +1 -0
  68. package/examples/hello/abaigram.yaml +24 -0
  69. package/examples/hello/hello.ts +18 -0
  70. package/package.json +40 -5
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Joon Hwa Jung
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,16 @@
1
- # Temporary Holding Version
1
+ # @abaigram/abaikit
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The abai authoring tool, itself an abaigram. A coding agent uses it to
4
+ make a program an abaigram under the contract, `spec/abaigrammar.md` in
5
+ the [abai repository](https://github.com/jjbits/abai). Installed with
6
+ `npm install --save-dev @abaigram/abaikit`, the command is `abaikit`.
7
+
8
+ - `abaikit learnabai` prints the guide the agent follows; `--skill`
9
+ installs it as a Claude Code skill.
10
+ - `abaikit check <repo>` verifies a repository's conformance and its
11
+ level; it needs no model.
12
+ - `abaikit new <name> --lang ts` creates a fresh abaigram.
13
+ - `abaikit abaify <repo>` retrofits an existing repository, adding and
14
+ never removing.
15
+ - `abaikit run <repo> -- ARGS` starts the repository's program.
16
+ - `abaikit abai ...` is the standard agent entry of abaikit itself.
@@ -0,0 +1 @@
1
+ You are the agent fused into abaikit, the abai authoring tool. You help a person create an abaigram, retrofit an existing repository into one, and verify conformance. You change a repository only through the repo.* capabilities, which add and never remove; you say why in every act; and you run check before you call anything done.
@@ -0,0 +1,546 @@
1
+ # learnabai: abai for a coding agent
2
+
3
+ abaigrammar {{abaigrammar}} · abai stub {{stub}} · abaikit {{abaikit}} · TypeScript
4
+
5
+ You are reading this because someone wants a program turned into an
6
+ abaigram, or a new one made, and you are the agent doing it. Read section
7
+ 1 once. Follow section 5 step by step; every step ends in a command that
8
+ proves it. Keep sections 2 and 8 at hand while you edit. `abaikit
9
+ learnabai --section <id>` prints one section; the ids are in the headings.
10
+
11
+ ## 1. What an abaigram is (what)
12
+
13
+ An abaigram is an ordinary program that includes the abai library (the
14
+ "stub") and thereby carries an agent: the agent ships with the program,
15
+ can run at install and during use, and observes and steers the program
16
+ only through channels the program declares. The contract is
17
+ `abaigrammar.md`; this guide is its TypeScript instantiation in practice.
18
+
19
+ Two halves, one boundary. The stub lives in the program. The harness is
20
+ the agent's runtime: a model, a tool set, a user channel. The boundary is
21
+ a function call inside the process (turns and thread modes) or a local
22
+ socket between two processes (sidecar mode). It means the same thing
23
+ either way.
24
+
25
+ Five channels, declared in a manifest the program builds in code:
26
+
27
+ - `events`: the program tells the agent something happened (push).
28
+ - `cells`: shared state the agent reads; a writable cell is a `knob` the
29
+ agent may set within its declared range.
30
+ - `capabilities`: functions the agent may call, each with a policy.
31
+ - `decisions`: the program stops and asks which way to go; every decision
32
+ has a default and a timeout, so the program never waits forever.
33
+ - `turns`: the program hands the agent a bounded moment to act.
34
+
35
+ Plus `lifecycle` (pause, resume, stop, restart, status: what a supervising
36
+ harness may do to the process), `attention` (what wakes the agent, and a
37
+ budget on it), and `network`, optional: the agent may meet the agents of
38
+ other programs on an agent network, and its `doc` sentence tells the agent
39
+ how the members it sees relate to this program's own names. abai carries
40
+ only the basic acts here (an identity on a network, who is on it, who is
41
+ reachable, a message sent to a member, a message received as a wake);
42
+ how agents find each other and what they say is each program's own, in
43
+ its declaration and its content.
44
+
45
+ Three modes. `turns`: one process, one thread, the agent runs only inside
46
+ a touchpoint; the simplest, the natural first rung for a command-line
47
+ program. `thread`: one process, the agent live beside the program once
48
+ it is attached (`await app.attach()` at startup, or any touchpoint), with
49
+ no touchpoint needed after that; in Node the harness runs on the
50
+ program's event loop between its callbacks, which the manifest states as
51
+ a deviation, so a long synchronous step on either side delays the other.
52
+ `sidecar`: the harness is a separate process, attached through a socket;
53
+ the reference mode, the one with the whole lifecycle channel. A manifest
54
+ declares the modes it supports; the first is the default; `ABAI_MODE`
55
+ overrides at startup.
56
+
57
+ Principles that bind everything you do:
58
+
59
+ 1. The program owns `main()`. The library is included; it wraps nothing
60
+ and replaces no entry point.
61
+ 2. The agent is invited: it acts through declared channels at declared
62
+ touchpoints and nowhere else.
63
+ 3. The program runs without the agent. No harness, a failed harness, a
64
+ missing model: the program is the plain program. Defaults everywhere.
65
+ 4. Declared, not discovered: the agent learns the program from the
66
+ manifest and its `doc` fields.
67
+ 5. Control has policy (`auto`, `confirm`, `forbid`) and leaves an audit
68
+ trail.
69
+ 6. It costs the program nothing it did not ask for: a detached channel
70
+ operation is a load or two; attached, nothing on the program's thread
71
+ allocates, locks or blocks except the touchpoints the program itself
72
+ placed.
73
+
74
+ How a person meets the agent: `<prog> abai` opens a session on the
75
+ terminal; `<prog> abai run -- ARGS` starts the program under the harness
76
+ with the whole lifecycle channel; `<prog> abai mcp` serves the agent's
77
+ tools to an MCP client such as Claude Code, whose own agent then is the
78
+ program's. All of that comes from a few inserted lines at the top of
79
+ `main()` (section 3).
80
+
81
+ ## 2. Declaring one in TypeScript (declare)
82
+
83
+ Install (section 5 has the exact command for this machine):
84
+
85
+ ```
86
+ npm install {{abai_spec}} {{harness_spec}}
87
+ ```
88
+
89
+ One module declares the abaigram and exports it. Everything below is
90
+ real API; copy what you need and delete the rest.
91
+
92
+ ```ts
93
+ // src/abai.ts
94
+ import { fileURLToPath } from "node:url";
95
+ import { createAbaigram } from "@abaigram/abai-stub";
96
+
97
+ export const app = createAbaigram(
98
+ {
99
+ name: "myprog", // the program's name; also the manifest's
100
+ entry: "node dist/main.js abai", // how the entry is reached: a runnable command line
101
+ modes: ["turns", "thread", "sidecar"], // supported; the first is the default
102
+ content: { identity: "abai/identity.md" }, // the prompts; identity is required
103
+ check: { run: "node dist/main.js --demo" }, // a short command that exercises the channels
104
+ cells: {
105
+ "queue.depth": { type: "int", access: "read", doc: "Jobs waiting." },
106
+ "worker.count": { // a knob: readwrite with a default and a range
107
+ type: "int", access: "readwrite", default: 4, range: [1, 64],
108
+ doc: "Active workers. The pool resizes at the next batch.",
109
+ },
110
+ },
111
+ events: {
112
+ "job.failed": {
113
+ payload: { job_id: { type: "string", max: 64 }, attempt: "int" },
114
+ wakes: "attempt gt 2", // "always", "never", or <field> eq|ne|gt|lt <json>
115
+ doc: "A job failed; past two attempts the agent is woken.",
116
+ },
117
+ },
118
+ capabilities: {
119
+ "job.retry": {
120
+ params: { job_id: "string" },
121
+ result: { ok: "bool" },
122
+ effect: "mutate", // "read" or "mutate"
123
+ policy: "confirm", // "auto" | "confirm" | "forbid"; mutate defaults to confirm
124
+ dispatch: "immediate", // run now. The default is "safepoint": the call waits for
125
+ // app.safepoint(), so a program that never calls safepoint()
126
+ // must declare "immediate", or the call stays pending and
127
+ // check never reaches L3
128
+ timeout: "30s",
129
+ example: { job_id: "job-0001" }, // check calls this; make it safe
130
+ doc: "Requeue a failed job once.",
131
+ handler: async ({ job_id }) => ({ ok: await retry(String(job_id)) }),
132
+ },
133
+ },
134
+ decisions: {
135
+ on_backlog: {
136
+ context: { depth: "int", workers: "int" },
137
+ options: ["scale_up", "shed_oldest", "wait"],
138
+ default: "wait",
139
+ timeout: "5s",
140
+ doc: "Asked when the backlog crosses the high-water mark.",
141
+ },
142
+ },
143
+ turns: {
144
+ end_of_batch: { timeout: "30s", doc: "After each batch. The agent may tune worker.count." },
145
+ },
146
+ lifecycle: {
147
+ pause: "cooperative", // "cooperative" | "os" | "none"
148
+ stop: { graceful: "10s", handler: () => shutdown() },
149
+ },
150
+ network: { // optional: the agent may be a member of an agent network
151
+ doc: "Members are other job queues of this fleet; a member's name is its host name.",
152
+ required: false, // true: a run with no membership attaches no agent
153
+ },
154
+ },
155
+ // the directory holding abaigram.yaml and abai/: one level up from src/abai.ts and from dist/abai.js;
156
+ // count the levels from the built file if the build nests deeper (dist/src/abai.js is two)
157
+ { root: fileURLToPath(new URL("..", import.meta.url)) },
158
+ );
159
+ ```
160
+
161
+ Using it from the program's own code, wherever the facts already are.
162
+ Obtain the handles once, at startup; `set`, `get` and `emit` are the cheap
163
+ calls. The import is in the repository's own style: `./abai.js` under
164
+ NodeNext (tsc resolves it to `abai.ts`), `./abai.ts` only where the project
165
+ runs or rewrites `.ts` imports.
166
+
167
+ ```ts
168
+ import { app } from "./abai.js";
169
+
170
+ const depth = app.cell<number>("queue.depth");
171
+ const workers = app.knob<number>("worker.count");
172
+ const failed = app.event("job.failed");
173
+
174
+ depth.set(queue.length); // one store, always cheap
175
+ const n = workers.get(); // the agent's setting, or the default
176
+ failed.emit({ job_id, attempt }); // nothing happens when no harness is attached
177
+ const choice = await app.decide<string>("on_backlog", { depth: queue.length, workers: n }); // "wait" when nobody answers
178
+ await app.turn("end_of_batch"); // returns at once when nobody is there
179
+ app.safepoint(); // where "safepoint" calls and a pause take effect
180
+ await app.attach(); // ask for the agent now: optional in turns mode, a thread program's first act
181
+ ```
182
+
183
+ abai is ESM-only. In a CommonJS repository load it inside the async main:
184
+ `const { createAbaigram, runEntry } = await import("@abaigram/abai-stub");` (Node 22.12 and
185
+ later can also `require("@abaigram/abai-stub")`). Under `verbatimModuleSyntax` the imports
186
+ above are all value imports and need no change; a type you import, such as
187
+ `Abaigram`, needs `import type`.
188
+
189
+ Types: `int`, `float`, `bool`, `enum` (with `enum: [...]`), `string`,
190
+ `object` (with `fields`), `list` (with `items`); sized types carry `max`.
191
+ A schema field is a bare type name or `{ type, max, enum, fields, items,
192
+ nullable, doc }`. A knob is `access: "readwrite"` with a `default`; a
193
+ scalar knob lands in shared state, a sized one is delivered to
194
+ `app.knob(name).onChange(handler)` at a safepoint. Capabilities may also
195
+ be bound late: `app.capability("job.retry", handler)`.
196
+
197
+ Policy (section 10 of the contract): `auto` runs on the agent's say,
198
+ `confirm` asks the person first (under an MCP client, the client's own
199
+ permission prompt is the person's consent), `forbid` is declared but never
200
+ runnable. Every act is written to the audit log with the agent's reason.
201
+
202
+ Cost: detached, every operation above is a load or a store. Keep your own
203
+ handlers cheap too; the agent's model calls never run on the program's
204
+ thread in sidecar mode, in turns mode they run only inside the
205
+ touchpoints you placed, and in thread mode the agent's own work runs on
206
+ the program's event loop between its callbacks, so a slow synchronous
207
+ step on either side delays the other.
208
+
209
+ ## 3. The standard entry (entry)
210
+
211
+ Every abaigram answers `<prog> abai ...`. The stub provides it; the
212
+ program hands it the arguments first thing in `main()`:
213
+
214
+ ```ts
215
+ import { runEntry } from "@abaigram/abai-stub";
216
+ import { app } from "./abai.js";
217
+
218
+ async function main(): Promise<void> {
219
+ const code = await runEntry(app, process.argv.slice(2));
220
+ if (code >= 0) {
221
+ process.exitCode = code; // an abai verb ran and said what it had to say
222
+ return;
223
+ }
224
+ // -1: no abai verb; the program's own argument handling follows, untouched
225
+ }
226
+ ```
227
+
228
+ In a module that already runs at top level, the same two statements stand
229
+ on their own: `const code = await runEntry(app, process.argv.slice(2)); if
230
+ (code >= 0) process.exit(code);`. Prefer `process.exitCode` where you can:
231
+ `process.exit` can cut a large `manifest --json` short on a pipe, and
232
+ `abaikit check` reads the entry through one.
233
+
234
+ Added lines, nothing removed: the program's own argument parsing follows
235
+ untouched, and a plain invocation reaches it exactly as before.
236
+
237
+ The verbs the entry serves:
238
+
239
+ ```
240
+ <prog> abai open a session (needs a configured model)
241
+ <prog> abai install run the install touchpoint
242
+ <prog> abai run -- ARGS sidecar launch: the harness starts "<prog> ARGS" as its child
243
+ <prog> abai config machine-level model and credential settings (~/.abai)
244
+ <prog> abai doctor offline: manifest, entry, content, modes; online: the model
245
+ <prog> abai manifest print the effective manifest
246
+ <prog> abai mcp serve the agent's tools to an MCP client over stdio
247
+ ```
248
+
249
+ The manifest exists twice and must agree: in code (the source of truth at
250
+ runtime) and as `abaigram.yaml` at the repository root, generated by the
251
+ program itself and checked in:
252
+
253
+ ```
254
+ <entry> manifest > abaigram.yaml
255
+ <entry> doctor --offline
256
+ ```
257
+
258
+ The `entry` field is a command line that runs in the repository root; the
259
+ tools reach the program through it. `node dist/main.js abai`, `npx myprog
260
+ abai`, `./bin/myprog abai` are all fine; `myprog abai` only once `myprog`
261
+ is on the PATH.
262
+
263
+ Content lives in `abai/` at the root unless the manifest says otherwise:
264
+ `abai/identity.md` (required: who the agent is for this program and how
265
+ it should behave), `abai/install.md`, `abai/use.md`, `abai/turns/<name>.md`.
266
+ The harness composes the system prompt from `identity`, the mode, and
267
+ every declaration rendered with its `doc`. Good `doc` fields are most of
268
+ the work of good content.
269
+
270
+ ## 4. Conformance levels and what check demands (levels)
271
+
272
+ No level needs a model. `abaikit check <repo>` verifies them with the
273
+ null harness, a harness that attaches and drives nothing (passive) or
274
+ drives every declared knob and capability by its declared default and
275
+ `example` (active, the "exercise run").
276
+
277
+ | Level | Name | Requires |
278
+ |---|---|---|
279
+ | L1 | reachable | The stub is included; `<entry> manifest` works; `abaigram.yaml` exists, validates and agrees with the code; `abai/identity.md` exists; the offline `doctor` passes. Checked without running the program beyond its entry. |
280
+ | L2 | observable | L1, plus `check.run`, plus at least one cell written or event emitted during the exercise run. |
281
+ | L3 | steerable | L2, plus one capability the exercise calls successfully with its `example`, or one knob whose write lands. |
282
+ | L4 | deliberative | L3, plus one decision the program reaches during the exercise run. |
283
+
284
+ L1 is what `<entry> doctor --offline` checks; `abaikit check` repeats it
285
+ from outside and adds the levels above.
286
+
287
+ `check.run` is a command, run by the shell in the repository root, that
288
+ exercises the program's channels and exits: a demo mode, a short batch, a
289
+ dry run. `check` runs it with `ABAI_HARNESS=exercise` (the active null
290
+ harness, which also stands in for the person's consent on `confirm`
291
+ acts) under each declared mode (`ABAI_MODE`), and reads the report the
292
+ stub prints at exit. It then measures: the same command with
293
+ `ABAI_HARNESS=off` (nothing attaches) and `ABAI_HARNESS=null` (the
294
+ passive harness), side by side; a declared `check.overhead` (such as
295
+ `"5%"`) fails the check when the null run is slower than that. Every
296
+ declaration the exercise could not reach is named in the report,
297
+ capabilities without an `example` included. The exercise and measurement
298
+ runs attach the null harness, so the audit log the manifest names
299
+ receives their records: keep that file out of version control, or point
300
+ `audit.path` outside the checkout.
301
+
302
+ ```
303
+ abaikit check <repo> # the level reached, modes, overhead, unexercised, violations
304
+ abaikit check <repo> --level 3 # exit non-zero below level 3
305
+ abaikit check <repo> --json
306
+ ```
307
+
308
+ A program is at the level `check` can demonstrate, not the level you
309
+ intended. If a channel is only reached by a path `check.run` does not
310
+ take, change `check.run`, not the claim.
311
+
312
+ ## 5. The retrofit, step by step (retrofit)
313
+
314
+ Follow the repository's own guidelines and conventions first: its
315
+ `AGENTS.md` or `CONTRIBUTING.md`, its module system (ESM or CommonJS,
316
+ TypeScript or JavaScript), its linter and formatter, its test command.
317
+ Everything you add is additive: new files, inserted lines, two added
318
+ dependencies. You never rewrite `main()`, never delete a line, never
319
+ reorder existing code.
320
+
321
+ Step 0. Know the program. Find the package manifest, the entry file the
322
+ `bin` or `main` points at, how it is built and run, and where its state
323
+ already lives (a queue, a counter, a config). Verify: you can name the
324
+ file where the program's own argument handling starts, and the command
325
+ that runs the program.
326
+
327
+ Step 1. Add the dependencies, in the repository root.
328
+
329
+ ```
330
+ npm install {{abai_spec}} {{harness_spec}}
331
+ ```
332
+ {{workspace_note}}
333
+ Verify, in the repository root:
334
+ `node -e "import('@abaigram/abai-stub').then(m => console.log(m.ABAI_STUB_VERSION))"`
335
+ prints `{{stub}}`.
336
+
337
+ Step 2. Declare the abaigram in a new module (`src/abai.ts`, or
338
+ `src/abai.js` in a JavaScript repository) as in section 2, with only
339
+ `name`, a runnable `entry`, `modes: ["turns", "sidecar"]` (add `"thread"`
340
+ when the program keeps running between touchpoints and wants the agent
341
+ live in its process; it then calls `await app.attach()` at startup),
342
+ `content` and `root` to begin with. When the program's real entry is a
343
+ plain launcher that runs without the build (a `bin/x.mjs` that imports
344
+ `dist/`), put the declaration beside it as a `.mjs` module the launcher
345
+ can import, so the entry answers `manifest` and `doctor` before anything
346
+ is built. Verify: build as the repository does, then
347
+ `node -e "import('./dist/abai.js')"` (the built path) runs without error.
348
+
349
+ Step 3. Hook the entry. In the file where `main()` starts, insert the
350
+ lines of section 3 before the program's own argument handling. The entry
351
+ runs the built program, so from here on every change is: build (the
352
+ repository's build script), then `<entry> manifest > abaigram.yaml`, then
353
+ verify. Verify: `<entry> manifest` prints YAML, and the program's own
354
+ usage (`<prog> --help`, or whatever it answered before) is unchanged.
355
+
356
+ Step 4. Write `abai/identity.md`: who the agent is for this program, what
357
+ it is for, how it should behave, in a few sentences. Verify:
358
+ `<entry> doctor --offline` prints an `identity` line that begins with `ok`.
359
+
360
+ Step 5. Generate the manifest file and reach L1.
361
+
362
+ ```
363
+ <entry> manifest > abaigram.yaml
364
+ abaikit check .
365
+ ```
366
+
367
+ Verify: the report says level 1. Commit `abaigram.yaml`, `abai/`, the new
368
+ module, the inserted lines, `package.json` and its lock file; nothing else
369
+ changed.
370
+
371
+ Step 6. Observe: L2. Declare one or two cells and events where the program
372
+ already has the facts (a depth, a count, a failure), and set or emit them
373
+ in the existing code paths, one line each. Add `check: { run: "<a short
374
+ command that exercises them>" }` to the declaration, a mode of the
375
+ program that runs for seconds and exits (a `--demo` flag you add, a small
376
+ fixed input, a dry run). Build, regenerate `abaigram.yaml`, verify: `abaikit check .
377
+ --level 2`.
378
+
379
+ Step 7. Steer: L3. Declare one knob the program already reads as a
380
+ setting (with its current value as the `default` and a safe `range`), or
381
+ one capability with a handler and an `example` whose call is harmless.
382
+ Build, regenerate, verify: `abaikit check . --level 3`; the report names
383
+ the knob as landed or the capability as called.
384
+
385
+ Step 8. Deliberate: L4. At a real fork in the program (retry or give up,
386
+ scale or wait), declare a decision with `options`, a `default` that is
387
+ what the program does today, and a short `timeout`; insert `const choice
388
+ = await app.decide("on_retry", { ... })` above the fork and add a branch
389
+ on `choice`, the existing path staying as the branch the default chooses.
390
+ Make sure `check.run` reaches the fork. Build, regenerate, verify:
391
+ `abaikit check . --level 4`.
392
+
393
+ Step 9. Meet the agent. With a model configured (`<entry> config set
394
+ provider ...`, `<entry> config set model ...`, `<entry> config set key
395
+ ...`, then `<entry> doctor`), open `<entry>` and ask what it sees; or run
396
+ `<entry> run -- ARGS` and watch it supervise; or serve `<entry> mcp` to
397
+ Claude Code (section 6). Say what you added and why in the commit
398
+ message and, if the repository keeps one, its changelog.
399
+
400
+ If `abaikit check` reports a violation, read its location and message:
401
+ they name the file or the declaration and what the contract wanted.
402
+
403
+ ## 6. Running an abaigram (use)
404
+
405
+ Session on the terminal, the person talking to the agent while the
406
+ program runs (turns and thread modes in-process; sidecar attaches to or
407
+ starts the harness binary):
408
+
409
+ ```
410
+ <entry>
411
+ ```
412
+
413
+ Launch under supervision, the harness as the parent with the whole
414
+ lifecycle channel (pause, resume, stop, restart, status) and the
415
+ program's output observed from outside:
416
+
417
+ ```
418
+ <entry> run -- ARGS
419
+ abaikit run <repo> -- ARGS # the same, from outside the repository
420
+ ```
421
+
422
+ An MCP client as the agent, no model configured anywhere: the client's
423
+ own agent uses the program's tools, and the client's permission prompt is
424
+ the person's consent.
425
+
426
+ ```json
427
+ { "mcpServers": { "myprog": { "command": "node", "args": ["/abs/path/to/dist/main.js", "abai", "mcp"] } } }
428
+ ```
429
+
430
+ (An absolute path, or run `claude` from the repository root: the client
431
+ starts the command from wherever it is.)
432
+
433
+ ```
434
+ claude -p "what is the program doing?" --mcp-config mcp.json --strict-mcp-config --allowedTools "mcp__myprog__*"
435
+ ```
436
+
437
+ The machine-level model configuration, shared by every abaigram on the
438
+ machine, lives under `~/.abai/`:
439
+
440
+ ```
441
+ <entry> config providers # every provider and the login it offers
442
+ <entry> config set provider <id>
443
+ <entry> config set model <id>
444
+ <entry> config login <provider> # sign in with an account; --api-key for a key, --browser opens the URL
445
+ <entry> config set key <provider> # a key, read without echo
446
+ <entry> config set url <provider> <base url>
447
+ <entry> doctor # offline checks, then the model
448
+ ```
449
+
450
+ Environment variables, all optional: `ABAI_MODE` (`turns`, `thread` or
451
+ `sidecar`, overriding the manifest's first mode), `ABAI_HARNESS` (`off`,
452
+ `null`, `exercise`: check runs only; never set it for real use),
453
+ `ABAI_HOME` (the configuration directory, default `~/.abai`),
454
+ `ABAI_RUN_DIR` (descriptors of running programs, default `~/.abai/run`),
455
+ `ABAI_HARNESS_BIN` (the sidecar harness binary to spawn),
456
+ `ABAI_HARNESS_MODULE` (the in-process harness module to import),
457
+ `ABAI_CHANNEL` (`terminal` or `none`), `ABAI_ATTACH_WAIT_MS` (how long a
458
+ touchpoint waits for a harness), and for the network below `ABAI_NET_DIR`,
459
+ `ABAI_NET_TOKEN` and `ABAI_AIIP_BIN`.
460
+
461
+ A program that declares `network` puts its agent on an aiip network
462
+ (`~/projects/aiip`: an overlay network for agents, one Go binary, no
463
+ root). The harness talks to aiip's daemon over its socket; the `aiip`
464
+ binary must be on the machine (on the PATH, or named by `ABAI_AIIP_BIN`
465
+ or `<entry> config set aiip-bin <path>`), and `doctor` says when it is
466
+ not. A run's identity lives in a state directory: `ABAI_NET_DIR`, else
467
+ `~/.abai/net/<name>/`, so two runs of one program on one machine are one
468
+ member unless the run is given a directory of its own. At attach the
469
+ harness finds or starts the daemon for that directory, joins with the
470
+ invite in `ABAI_NET_TOKEN` when the run is not yet a member, and tells
471
+ the agent where it is; `abai_peers` lists who else is there and who is
472
+ reachable now. A message from a member wakes the agent as the event
473
+ `abai.net.message`, with the sender's issued name and what it said;
474
+ `abai_send` answers a member by name, with a reason, and `abai_messages`
475
+ lists the run's messages. Every message sent or received is one line in
476
+ the audit log (`act: message`), written by the stub. The envelope on the
477
+ wire is aiap's (`~/projects/agents-cowork`), so an abaigram and an aiap
478
+ agent on one network read each other. A declared network the run cannot
479
+ have is said in the prompt, unless `required` is true, in which case no
480
+ agent attaches.
481
+
482
+ ```
483
+ <entry> config net # the state dir, the daemon, the memberships
484
+ <entry> config net create <network> --as <member> # a new network, this run as its authority
485
+ <entry> config net invite <network> --for <member> # an invite token for a new member (authority only)
486
+ <entry> config net join --token - # join with an invite read from stdin
487
+ <entry> config net leave <network>
488
+ ```
489
+
490
+ An invite token carries the member's private key: hand it to that
491
+ member's run as `ABAI_NET_TOKEN` or on stdin, never in a command line, a
492
+ file or a log, and never over a link that is not encrypted end to end to
493
+ that run. A network has 253 slots besides its authority and no removal:
494
+ every name ever invited takes one, whether or not its holder enrolls, and
495
+ a name whose holder has enrolled is that member's for good. Keep a
496
+ member's state directory, since a lost one cannot rejoin under its name,
497
+ and let the member run now and then: a certificate lasts 30 days and the
498
+ daemon renews it only while it runs, so a member that has not run for 30
499
+ days is locked out just the same. An invite for a name whose holder never
500
+ enrolled reissues that slot with fresh credentials and voids the earlier
501
+ token, so mint a second invite for a name only when the authority's
502
+ roster (`abai_peers`, or the harness client's `members`) shows its holder
503
+ not enrolled and nobody is about to enroll with the first. Who creates
504
+ the network, who mints the invites, how members find each other and what
505
+ they say to one another is the program's to decide, in its code and its
506
+ content, not abai's.
507
+
508
+ The audit log (`audit.path` in the manifest; default
509
+ `~/.abai/audit/<name>.jsonl`) records every act of control with its
510
+ reason, consent and outcome. Read it when you wonder what the agent did.
511
+
512
+ ## 7. Where this instantiation departs from the contract (deviations)
513
+
514
+ The first four are in every manifest this stub writes, the fifth when
515
+ `thread` is declared and the last when `sidecar` is, so an agent reading
516
+ the manifest sees them too:
517
+
518
+ {{deviations}}
519
+
520
+ ## 8. What never to do (never)
521
+
522
+ - Never rewrite, wrap or replace `main()`. The entry hook is a few
523
+ inserted lines at its top; the program's own flow follows them.
524
+ - Never delete or reorder existing lines. A retrofit is additive; a
525
+ reviewer should see only added lines and new files.
526
+ - Never put the stub behind a condition. The entry must be reachable on
527
+ every invocation, or `check` cannot reach L1.
528
+ - Never leave a capability on the default `safepoint` dispatch in a
529
+ program that never calls `app.safepoint()`: the call stays pending and
530
+ `check` never reaches L3.
531
+ - Never block in a handler or a `set`. Long work belongs behind an
532
+ `immediate`-dispatched async capability, or in a job the handler
533
+ enqueues.
534
+ - Never declare an `example` whose call changes real state. The exercise
535
+ run calls it; so will a curious agent.
536
+ - Never declare a knob without a safe `default` and `range`, a decision
537
+ without the `default` the program chooses today, or a turn without a
538
+ `timeout`.
539
+ - Never put secrets or credentials in content or `doc` fields; they are
540
+ the prompt.
541
+ - Never claim a level `check` did not report, and never change a level by
542
+ editing `abaigram.yaml` by hand: it is generated, and `check` compares
543
+ it with the code.
544
+ - Never make `check.run` depend on the network, credentials, or state it
545
+ does not create itself.
546
+ - Never touch `node_modules`, build output, or files `.gitignore` names.
package/abai/use.md ADDED
@@ -0,0 +1 @@
1
+ A session with abaikit is about one repository at a time. Start by listing it and reading its package manifest and entry file; propose before you write; after every write, run check and read its report.