@xingular/harness 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +269 -2
- package/dist/cli.js +11510 -0
- package/dist/terminal-pi.js +326 -0
- package/package.json +41 -4
- package/src/accounts/accounts.test.ts +223 -0
- package/src/accounts/accounts.ts +522 -0
- package/src/accounts/env.test.ts +95 -0
- package/src/accounts/env.ts +338 -0
- package/src/accounts/secrets.ts +50 -0
- package/src/accounts/store.ts +338 -0
- package/src/capabilities/api.ts +75 -0
- package/src/capabilities/runSnapshot.ts +59 -0
- package/src/capabilities/types.ts +34 -0
- package/src/cli.ts +110 -0
- package/src/core/bus.ts +34 -0
- package/src/core/changes.ts +33 -0
- package/src/core/env.ts +27 -0
- package/src/core/errors.ts +32 -0
- package/src/core/frontmatter.ts +47 -0
- package/src/core/fs.test.ts +38 -0
- package/src/core/fs.ts +92 -0
- package/src/core/index.ts +193 -0
- package/src/core/path.ts +45 -0
- package/src/core/projects.ts +90 -0
- package/src/core/text.ts +39 -0
- package/src/harness.ts +299 -0
- package/src/index.ts +70 -0
- package/src/mcp/connections.ts +614 -0
- package/src/mcp/manager.ts +130 -0
- package/src/mcp/oauth.ts +195 -0
- package/src/mcp/scopeMigration.ts +204 -0
- package/src/mcp/service.test.ts +376 -0
- package/src/mcp/service.ts +549 -0
- package/src/mcp/types.ts +108 -0
- package/src/memory/api.test.ts +82 -0
- package/src/memory/api.ts +289 -0
- package/src/memory/blocks.test.ts +78 -0
- package/src/memory/blocks.ts +406 -0
- package/src/memory/caller.ts +57 -0
- package/src/memory/dispatch.ts +59 -0
- package/src/memory/facts.test.ts +41 -0
- package/src/memory/facts.ts +248 -0
- package/src/memory/graph.ts +103 -0
- package/src/memory/health.test.ts +130 -0
- package/src/memory/health.ts +74 -0
- package/src/memory/maintainer.ts +272 -0
- package/src/memory/maintenance.test.ts +176 -0
- package/src/memory/maintenance.ts +330 -0
- package/src/memory/migrate.ts +79 -0
- package/src/memory/operations.ts +138 -0
- package/src/memory/proposals.test.ts +168 -0
- package/src/memory/proposals.ts +576 -0
- package/src/memory/recall.test.ts +104 -0
- package/src/memory/recall.ts +271 -0
- package/src/memory/references.ts +77 -0
- package/src/memory/runContext.ts +93 -0
- package/src/memory/secrets.ts +6 -0
- package/src/memory/store.test.ts +66 -0
- package/src/memory/store.ts +385 -0
- package/src/memory/wiki.test.ts +47 -0
- package/src/memory/wiki.ts +422 -0
- package/src/memory/wikiIndex.test.ts +88 -0
- package/src/memory/wikiIndex.ts +116 -0
- package/src/plugins/api.ts +181 -0
- package/src/plugins/attachments.test.ts +84 -0
- package/src/plugins/attachments.ts +141 -0
- package/src/plugins/builtin.ts +82 -0
- package/src/plugins/catalogue.test.ts +254 -0
- package/src/plugins/catalogue.ts +251 -0
- package/src/plugins/cli.ts +184 -0
- package/src/plugins/discover.ts +151 -0
- package/src/plugins/firstParty.test.ts +143 -0
- package/src/plugins/firstParty.ts +129 -0
- package/src/plugins/hooks.test.ts +250 -0
- package/src/plugins/hooks.ts +239 -0
- package/src/plugins/identity.ts +37 -0
- package/src/plugins/install.test.ts +107 -0
- package/src/plugins/install.ts +106 -0
- package/src/plugins/inventory.ts +123 -0
- package/src/plugins/manifest.ts +76 -0
- package/src/plugins/plugins.test.ts +205 -0
- package/src/plugins/plugins.ts +189 -0
- package/src/plugins/proposal.ts +121 -0
- package/src/plugins/runtime.test.ts +40 -0
- package/src/plugins/runtime.ts +82 -0
- package/src/plugins/trust.ts +142 -0
- package/src/plugins/types.ts +149 -0
- package/src/prompt/compile.test.ts +261 -0
- package/src/prompt/compile.ts +732 -0
- package/src/server/methods.ts +314 -0
- package/src/server/remote.ts +77 -0
- package/src/server/rpc.ts +116 -0
- package/src/server/serve.ts +28 -0
- package/src/server/server.test.ts +174 -0
- package/src/sessions/approvals.ts +118 -0
- package/src/sessions/contracts.ts +299 -0
- package/src/sessions/conversation.test.ts +150 -0
- package/src/sessions/extensionSession.test.ts +74 -0
- package/src/sessions/extensionUi.test.ts +65 -0
- package/src/sessions/extensionUi.ts +102 -0
- package/src/sessions/history.ts +51 -0
- package/src/sessions/lineageJoin.ts +75 -0
- package/src/sessions/policy.ts +32 -0
- package/src/sessions/pricing.ts +60 -0
- package/src/sessions/reasoningSummaries.ts +59 -0
- package/src/sessions/registry.ts +294 -0
- package/src/sessions/runIndex.ts +99 -0
- package/src/sessions/runStore.test.ts +152 -0
- package/src/sessions/runStore.ts +332 -0
- package/src/sessions/runner.ts +556 -0
- package/src/sessions/runtimeResources.test.ts +61 -0
- package/src/sessions/sessions.test.ts +434 -0
- package/src/sessions/sessions.ts +295 -0
- package/src/skills/registry.test.ts +104 -0
- package/src/skills/registry.ts +407 -0
- package/src/terminal/cli.ts +149 -0
- package/src/terminal/client.ts +74 -0
- package/src/terminal/controller.ts +227 -0
- package/src/terminal/piExtension.ts +208 -0
- package/src/terminal/pluginControl.test.ts +146 -0
- package/src/terminal/pluginMenu.ts +112 -0
- package/src/terminal/socket.ts +182 -0
- package/src/terminal/standalone.test.ts +115 -0
- package/src/terminal/standalone.ts +126 -0
- package/src/terminal/terminal.test.ts +302 -0
- package/src/terminal/terminals.ts +429 -0
- package/src/tools/askUser.ts +45 -0
- package/src/tools/definitions.ts +187 -0
- package/src/tools/memoryTools.ts +22 -0
- package/src/tools/pluginPropose.ts +41 -0
- package/src/tools/types.ts +36 -0
- package/src/web/safeRequest.ts +171 -0
- package/src/web/text.ts +53 -0
- package/src/web/tools.ts +134 -0
- package/src/web/web.test.ts +141 -0
package/README.md
CHANGED
|
@@ -1,3 +1,270 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Xingular Harness
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A Pi agent harness, as one TypeScript package (`@xingular/harness`). It is everything a model
|
|
4
|
+
needs to work for someone over time, and nothing about any particular application:
|
|
5
|
+
|
|
6
|
+
| Part | Folder | What it does |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| Accounts | `src/accounts/` | Several isolated Pi accounts and pools, sign-in, model lists, health and limits. A session keeps the account it started with. |
|
|
9
|
+
| Sessions | `src/sessions/` | Pi SDK turns, run records and `events.jsonl`, interrupt, approvals, questions, recovery after a crash. |
|
|
10
|
+
| Prompt | `src/prompt/` | The system prompt compiled from sections with budgets and profiles; a stable prefix, and changing state delivered as updates in native history. |
|
|
11
|
+
| Memory | `src/memory/` | The Wiki: pages, facts, memory blocks, revisions, proposals with an evidence gate, undo, an Inbox, recall over past runs, and sleep-time consolidation. |
|
|
12
|
+
| Skills | `src/skills/` | Skills, prompts, and rules: a person's own in the data root, and built-in ones shipped with the harness or the application. |
|
|
13
|
+
| Plugins | `src/plugins/` | What a Pi session loads, chosen by the harness and never by Pi's own discovery; trust for a working folder's code; the list of every plugin and what reaches one caller. |
|
|
14
|
+
| Tools | `src/tools/` | Tool definitions, the memory tools, `ask_user`; a gate that asks the application's policy, then standing approvals, then the person. |
|
|
15
|
+
| Connected apps | `src/mcp/` | MCP connections, OAuth, per-session grants, redaction, a trace of what each call returned. |
|
|
16
|
+
| Terminal | `src/terminal/` | Interactive Pi sessions with isolated accounts, compiled instructions, a telemetry and tool extension, and the session socket with the `agentx-wiki` and `agentx-mcp` commands. |
|
|
17
|
+
| Server | `src/server/` | The same API over newline-delimited JSON, for a host that is not a Node program. |
|
|
18
|
+
|
|
19
|
+
It was split out of Xingular Agent, which is now an application built on it.
|
|
20
|
+
|
|
21
|
+
## Using it from Node
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { createHarness } from '@xingular/harness'
|
|
25
|
+
|
|
26
|
+
const harness = createHarness({
|
|
27
|
+
dataRoot: '/path/to/data', // knowledge and history: wiki/, registry/, runs/, index.sqlite
|
|
28
|
+
stateDir: '/path/to/state', // device-local: accounts, approvals, connections, sockets
|
|
29
|
+
productName: 'My App', // how the harness names itself to a model (default 'Xingular Harness')
|
|
30
|
+
secrets, // SecretStore; defaults to an owner-only file in stateDir
|
|
31
|
+
policy, // ToolPolicy: what a run may do, and what needs a person
|
|
32
|
+
directory, // names for the ids the application uses
|
|
33
|
+
builtins: ['/path/to/builtin'], // folders of built-in skills, prompts, and rules
|
|
34
|
+
plugins: [myPlugin] // the application's own parts, described as plugins
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
harness.sessions.provide((session) => myTools(session)) // the application's own tools
|
|
38
|
+
harness.runs.listen((event, run) => show(event)) // every run event, after it is saved
|
|
39
|
+
|
|
40
|
+
await harness.sessions.run({
|
|
41
|
+
run: { id, sessionId },
|
|
42
|
+
cwd, sessionDir, text,
|
|
43
|
+
caller: { agentId, agentName, actor: 'Chat' },
|
|
44
|
+
launch: { sections: [{ id: 'identity', title: 'Identity', body, budget: 2000, order: 10 }] },
|
|
45
|
+
tools: { allow: ['read', 'bash', 'my_tool'], gated: ['bash'] }
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## How an application extends it
|
|
50
|
+
|
|
51
|
+
The harness knows a caller (an opaque id that owns memory), never an agent, a group, or a task.
|
|
52
|
+
An application adds what it knows through six points:
|
|
53
|
+
|
|
54
|
+
1. **Prompt sections** (`Launch.sections`, `Launch.toolGroups`, `Launch.references`). Each
|
|
55
|
+
section has an `order` and may be `dynamic`. The harness adds its own (`blocks` 30, `rules` 40,
|
|
56
|
+
`references` 50, `skills` 60, `knowledge` 80, `tools` 90), applies budgets and the caller's
|
|
57
|
+
context profile, keeps stable sections in the system prompt, and sends dynamic ones as
|
|
58
|
+
updates.
|
|
59
|
+
2. **Tool providers** (`harness.sessions.provide`). A provider returns `ToolSpec`s for a session;
|
|
60
|
+
only the names in the session's `allow` list are registered, in that order.
|
|
61
|
+
The harness provides the web tools itself: `webTools({ secrets })` gives `web_search` (Brave Search,
|
|
62
|
+
key read from the secret store) and `web_fetch` (checked reads of public pages). An application
|
|
63
|
+
passes it to `harness.sessions.provide` and switches the names on for each agent.
|
|
64
|
+
3. **`ToolPolicy`**. `scope` says whether a call is inside what the run may do (or needs one
|
|
65
|
+
approval); `authorize` says whether the tool is still switched on; `approval` adds facts for
|
|
66
|
+
standing approvals; `connections` narrows the connected apps a session reaches.
|
|
67
|
+
4. **`SecretStore`**. Where credentials live (a keychain, a vault). Keys are
|
|
68
|
+
`<account id>/<name>`.
|
|
69
|
+
5. **`Directory`**. Names for the application's ids, shown in what the harness writes.
|
|
70
|
+
6. **Plugin descriptions** (`HarnessOptions.plugins`). Each `PluginDescriptor` names one part of
|
|
71
|
+
the application and says which tools, prompt sections and tool groups it provides. It is data:
|
|
72
|
+
it changes what `plugins.list` and `plugins.inventory` report, never what runs. `WEB_PLUGIN`
|
|
73
|
+
describes the web tools. A host that is not a Node program sends them with `plugins.describe`.
|
|
74
|
+
|
|
75
|
+
`apps.expose(...)` additionally offers the application's tools through the connected-apps
|
|
76
|
+
broker (`mcp_list_tools`, `mcp_call_tool`), which is how a terminal session reaches them.
|
|
77
|
+
|
|
78
|
+
## Plugins: what a session loads
|
|
79
|
+
|
|
80
|
+
A plugin is a bundle with a title, a description and contributions: instructions, skills,
|
|
81
|
+
commands, tools, connections, code (a Pi extension) and themes. A Pi package is a plugin as it
|
|
82
|
+
is; so are the harness's own parts (Memory, Wiki, History, Ask the user, Connected apps, Pi), the
|
|
83
|
+
application's, a person's registry ("Yours"), the folder every account shares, and what a
|
|
84
|
+
working folder brings.
|
|
85
|
+
|
|
86
|
+
- **The harness is the only loader.** A session opens Pi with its own discovery switched off and
|
|
87
|
+
the paths the harness chose (`Plugins.loading`); a terminal gets the same list as `--extension`,
|
|
88
|
+
`--skill`, `--prompt-template` and `--theme` flags after `--no-extensions --no-skills
|
|
89
|
+
--no-prompt-templates --no-themes`. The harness finds things with Pi's own resolver, which runs
|
|
90
|
+
no extension code. Pi still reads `AGENTS.md` and `SYSTEM.md` itself; they are listed.
|
|
91
|
+
- **Code waits for trust.** A working folder's `.pi/extensions` and the packages its settings
|
|
92
|
+
name are trusted together; each installed package is trusted on its own. Trust is a hash of the
|
|
93
|
+
files (`plugins.trust { hash }`), so the same code in another folder is trusted and a changed
|
|
94
|
+
file asks again. A loose file a person put in the shared `extensions/` folder loads without
|
|
95
|
+
asking. The first time this version runs, the packages already present are recorded as trusted,
|
|
96
|
+
once; anything installed or changed later waits.
|
|
97
|
+
- **Installing runs no code.** `plugins.install { source }` takes a folder, an npm name or a git
|
|
98
|
+
address and goes through Pi's own package manager on the settings every account shares, so
|
|
99
|
+
`pi list` agrees. Install scripts are switched off (`npm_config_ignore_scripts`). The package
|
|
100
|
+
arrives switched off and, with code, not trusted. `plugins.remove` and `plugins.update` take a
|
|
101
|
+
`pluginId`. A folder under `<data root>/plugins/` is found as a package where it is, and
|
|
102
|
+
removing it moves it to `<data root>/trash/plugins/`. `npm` and `git` in an account's Pi folder
|
|
103
|
+
are links to the shared folder, so one install serves every account.
|
|
104
|
+
- **Extensions have an interface without a terminal.** An SDK session that loaded an extension
|
|
105
|
+
binds it the way Pi's RPC mode does: `session_start` fires, and `select`, `confirm`, `input`
|
|
106
|
+
and `editor` become the run's question events; `notify`, `setStatus` and `setWidget` become
|
|
107
|
+
`extension.notice` run events. What needs a terminal (custom components, key handlers, themes)
|
|
108
|
+
does nothing. A tool an extension registered is offered to the model and asks before every
|
|
109
|
+
call. A command it registered runs when a message starts with it.
|
|
110
|
+
- **The gate runs last.** In an SDK session it is an inline extension, loaded after those on
|
|
111
|
+
disk; in a terminal the harness's extension is the last `--extension`. A plugin that changes a
|
|
112
|
+
tool call's input does so before the gate sees it.
|
|
113
|
+
- **A required part can be extended.** A package names the part it adds to and, optionally, a
|
|
114
|
+
hooks file: `"harness": { "extends": ["memory"], "hooks": "./hooks.mjs" }`. The harness runs
|
|
115
|
+
hooks itself (`src/plugins/hooks.ts`), in its own process, because most memory is written while
|
|
116
|
+
no session is open. `export default function (harness) { harness.on('memory.saved', async
|
|
117
|
+
(event, ctx) => …) }`; the events are `memory.saved`, `memory.block.changed`,
|
|
118
|
+
`wiki.page.saved` and `history.run.ended`. A hook is told after the write and cannot stop it,
|
|
119
|
+
has 30 seconds, and what it throws is kept in `plugin-hooks.json` and shown on its plugin. It
|
|
120
|
+
runs only while its plugin is switched on for everyone and trusted; what is loaded is checked
|
|
121
|
+
against that before each delivery. `ctx.call(connection, tool, args)` reaches a connected app
|
|
122
|
+
that is on for everyone, and only a tool marked to run without asking. A working folder's
|
|
123
|
+
package cannot bring a hook. An embedding application adds no hook events of its own yet.
|
|
124
|
+
- **An agent can propose a plugin, and only propose.** `plugin_propose` parks a set of files in
|
|
125
|
+
the Inbox. Accepting writes `<data root>/plugins/<name>/` with a `package.json` the harness
|
|
126
|
+
wrote, switched off and untrusted. No tool accepts, switches on or trusts.
|
|
127
|
+
- **Two plugins ship with the harness, switched off.** `output-trim` cuts a tool result longer
|
|
128
|
+
than its limit to its beginning and its end before the model reads it; `budget-guard` refuses
|
|
129
|
+
tool calls once a run passed a limit of model requests or tokens, leaves the agent one request
|
|
130
|
+
to wrap up, and ends the run if it calls tools again (`src/plugins/firstParty.ts`). They are
|
|
131
|
+
Pi extensions loaded inline, so they also run in unattended work and need no trust. A person
|
|
132
|
+
switches them like any plugin and sets their numbers with `plugins.configure { pluginId,
|
|
133
|
+
settings }`. A descriptor may declare `switchable`, `settings` and `code` to describe a part
|
|
134
|
+
like these. A terminal session does not load them yet.
|
|
135
|
+
- **A person can do the same from a terminal.** In a terminal session, `/plugins` lists what the
|
|
136
|
+
session loads and switches a plugin, or one thing it brings, for the session's project or for
|
|
137
|
+
everyone, and trusts code after showing its files. The session's shell can read the session
|
|
138
|
+
token from its environment, so a change is taken only with a second key that the harness's
|
|
139
|
+
extension claims once as it loads (`control.claim`) and keeps in its own memory; the socket's
|
|
140
|
+
`plugins.list` is open to the session, and `plugins.attach`, `plugins.detach`, `plugins.trust`
|
|
141
|
+
and `plugins.untrust` refuse without that key. A change applies when the session restarts.
|
|
142
|
+
Outside a session, `xingular-harness plugins list | show | install | remove | update | attach |
|
|
143
|
+
detach | trust | untrust` acts directly, as the person who typed it; `trust` shows the files
|
|
144
|
+
and wants a typed yes or `--yes`. It does not open the data root, so it settles no run.
|
|
145
|
+
- **Unattended runs and memory maintenance load no extension**, as before.
|
|
146
|
+
- **A person switches things on and off for a scope.** `plugins.attach` and `plugins.detach`
|
|
147
|
+
take a plugin's or a contribution's `id` and a `scope` (`{}` for everywhere, `{ agentId }`, or
|
|
148
|
+
`{ projectId }`). The narrowest scope that says something wins: an agent, then a project, then
|
|
149
|
+
everywhere. The choices are one file, `config/plugin-attachments.json`; registry enablement
|
|
150
|
+
(`skills.setEnabled`) reads and writes the same file, and the earlier `registry-enabled.json`
|
|
151
|
+
is read until the first change. A connected app keeps its scope in `connections.json` and is
|
|
152
|
+
switched through the same two methods. A switched-off extension, skill or prompt is not
|
|
153
|
+
loaded for that caller. The harness's own parts are required and refuse with their reason; an
|
|
154
|
+
application's parts are switched by the application. Switching something on grants no
|
|
155
|
+
permission: its tools still pass the gate.
|
|
156
|
+
- `plugins.list { cwd?, accountId?, agentId?, projectId? }` returns every plugin, with where each
|
|
157
|
+
is switched on (`attachment`); with an agent or a project, each state is what that caller
|
|
158
|
+
gets. `plugins.inventory { context, launch, tools, connections, cwd? }` returns what reaches
|
|
159
|
+
one caller, each item attributed to a plugin, with `unattributed` naming any prompt section,
|
|
160
|
+
tool group or tool no plugin accounts for.
|
|
161
|
+
|
|
162
|
+
A terminal session's `pi` command has four extensions of its own (`builtin:mcp`, `codemode`,
|
|
163
|
+
`tool-search`, `llama.cpp`). They are passed by name unless Pi's settings switch them off. An SDK
|
|
164
|
+
session does not have them.
|
|
165
|
+
|
|
166
|
+
## Using it from another language
|
|
167
|
+
|
|
168
|
+
```sh
|
|
169
|
+
npm run build
|
|
170
|
+
node dist/cli.js serve --state <dir> --data <dir> --name "My App" \
|
|
171
|
+
[--builtins <dir>] [--host-secrets] [--host-policy] [--host-tools] [--host-directory]
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Lines on stdin and stdout, one JSON object each:
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
host → harness { v: 1, id, method, params } harness → host { v: 1, id, result | error }
|
|
178
|
+
harness → host { v: 1, event } a run event
|
|
179
|
+
harness → host { v: 1, notice } state changed: read it again
|
|
180
|
+
harness → host { v: 1, id, call, params } host → harness { v: 1, id, result | error }
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`harness.methods` lists every method; [`src/server/methods.ts`](src/server/methods.ts) is the
|
|
184
|
+
table. With the `--host-*` flags the harness calls the host for `secrets.get|set|delete`,
|
|
185
|
+
`policy.scope|authorize|approval|connections`, and `tools.call`; the host registers its tools
|
|
186
|
+
with `tools.register` and its names with `directory.set`.
|
|
187
|
+
|
|
188
|
+
`skills.import` accepts complete user-owned skill folders:
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
{ skills: [{ name, files: [{ path, content, encoding }] }] }
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
It validates bounded relative paths and file encodings, requires a root
|
|
195
|
+
`SKILL.md`, rejects existing items, and writes the folders without enabling or executing them. It
|
|
196
|
+
has no knowledge of the application feature that may call it.
|
|
197
|
+
|
|
198
|
+
`npm run prove` starts that server on a throwaway data root, with no application present, and
|
|
199
|
+
exercises accounts, memory, skills, the prompt, connected apps, and a recorded session.
|
|
200
|
+
|
|
201
|
+
## Run it as a standalone Pi terminal
|
|
202
|
+
|
|
203
|
+
After the CLI package is published to npm or your company's npm registry, install and launch it:
|
|
204
|
+
|
|
205
|
+
```sh
|
|
206
|
+
npm install --global @xingular/harness
|
|
207
|
+
xingular-harness
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The machine needs Node 22.13 or newer and the `pi` command on `PATH`. On first launch, the
|
|
211
|
+
harness creates a `Default` Pi account; Pi handles provider sign-in.
|
|
212
|
+
|
|
213
|
+
To build and run from a source checkout instead:
|
|
214
|
+
|
|
215
|
+
```sh
|
|
216
|
+
npm install
|
|
217
|
+
npm run build
|
|
218
|
+
node dist/cli.js
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
This opens Pi's interactive terminal in the current working directory. The harness adds its
|
|
222
|
+
compiled context, isolated account, approval gate, Wiki commands, and terminal extension while
|
|
223
|
+
Pi provides the interface.
|
|
224
|
+
|
|
225
|
+
The default data and device state directories are `~/.xingular-harness/data` and
|
|
226
|
+
`~/.xingular-harness/state`. Override them with `--data <dir>` and `--state <dir>`. Session
|
|
227
|
+
history is stored per working directory and can be reopened with Pi's `/resume` command. The
|
|
228
|
+
explicit `terminal` command is an alias for the default launch. Use `--help` to see the commands;
|
|
229
|
+
`serve` remains the host-facing RPC service for integrations such as Xingular Agent/App.
|
|
230
|
+
|
|
231
|
+
## Storage
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
<data root>/
|
|
235
|
+
wiki/ home.md, library/, projects/<slug>/, facts/, blocks/, revisions, inbox, log
|
|
236
|
+
registry/ skills/, prompts/, rules/ (a person's own; never copied)
|
|
237
|
+
plugins/<name>/ packages kept with a person's data; each is found as an installed plugin
|
|
238
|
+
runs/<id>/ run.json, events.jsonl, context.json, caller.json
|
|
239
|
+
config/ context profiles, what is switched on for whom (plugin-attachments.json),
|
|
240
|
+
settings the harness reads
|
|
241
|
+
index.sqlite a rebuildable index of Wiki entries and runs (FTS5)
|
|
242
|
+
projects.json read only: the application owns it
|
|
243
|
+
|
|
244
|
+
<state dir>/
|
|
245
|
+
accounts/ registry, per-account Pi agent folders, audit log
|
|
246
|
+
approvals.json standing approvals
|
|
247
|
+
plugin-trust.json the code a person trusted, by hash, and whether earlier packages were taken over
|
|
248
|
+
plugin-registered.json what each extension registered the last time a session loaded it
|
|
249
|
+
plugin-hooks.json what each hooks file listens for and how its last run went
|
|
250
|
+
connections.json connected apps (secrets are in the SecretStore)
|
|
251
|
+
run/ the session socket and the terminal commands
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
The files are the source of truth; `index.sqlite` can be deleted and rebuilt
|
|
255
|
+
(`harness.rebuildIndex()`).
|
|
256
|
+
|
|
257
|
+
## Commands
|
|
258
|
+
|
|
259
|
+
```sh
|
|
260
|
+
npm run typecheck
|
|
261
|
+
npm test # unit tests and the golden prompt/memory fixtures in tests/golden
|
|
262
|
+
npm run build # dist/cli.js (the xingular-harness command) and dist/terminal-pi.js
|
|
263
|
+
npm run prove # the standalone proof above
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Node 22.13 or newer (for `node:sqlite`).
|
|
267
|
+
|
|
268
|
+
## Images
|
|
269
|
+
|
|
270
|
+
`SessionRunInput.requiresImageInput` optionally requires a vision-capable resolved Pi model before a prompt is delivered. The check includes direct `images` input, and the generic tool wrapper rejects image results on text-only models. Direct prompt images use Pi's `{ type: 'image', data, mimeType }` content shape. Applications own file formats, attachment selection, extraction and image preparation; the Harness only transports typed image content and validates model capability.
|