@tangle-network/harness-sessions 0.2.0 → 0.2.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 +13 -3
- package/dist/formats/kimi.js +20 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ npm install @tangle-network/harness-sessions
|
|
|
15
15
|
- The sidecar's harness session contract (agent-container `packages/cli-agent-registry`) imports `STORES` and `SessionFormatId` from here; it keeps no second copy of where a harness writes.
|
|
16
16
|
- The capture path reads captures through `listCapture` / `readCapture`.
|
|
17
17
|
- Discovery's trace gate, agent-eval, agent-record and the blog read sessions only through this package.
|
|
18
|
-
- Its
|
|
18
|
+
- Its dependencies are `@tangle-network/agent-core`, `@tangle-network/agent-trace-contract` and Node built-ins (`node:sqlite` for OpenCode).
|
|
19
19
|
|
|
20
20
|
## Formats
|
|
21
21
|
|
|
@@ -25,6 +25,8 @@ npm install @tangle-network/harness-sessions
|
|
|
25
25
|
| `codex` | `codex.rollout-jsonl` | `rollout-*-{id}.jsonl` under `sessions/` and `archived_sessions/` of every Codex home (below) | turn context (the configured model) | per response (`last_token_usage`); session total from `total_token_usage` |
|
|
26
26
|
| `opencode` | `opencode.sqlite` | `.local/share/opencode/opencode.db` (shared by every session) | session record (`providerID/modelID`) | per step (`step-finish`) |
|
|
27
27
|
| `pi` | `pi.session-jsonl` | `.pi/agent/sessions/**/*_{id}.jsonl` | session record (`provider/model`) | per assistant message |
|
|
28
|
+
| `kimi-code` | `kimi.session-dir` | `.kimi/sessions/*/{id}/wire.jsonl`, child wires at `*/{id}/subagents/*/wire.jsonl` | none in the wire | per step, excluding wrapped child events |
|
|
29
|
+
| `factory-droids` | `factory.session-jsonl` | `.factory/sessions/*/{id}.jsonl` plus settings | configured model in settings (`turn-context`) | session total in settings |
|
|
28
30
|
|
|
29
31
|
A HOME can hold several Codex homes, and `codexReader.locate` reads all of them (`codexHomes(home)` lists them):
|
|
30
32
|
|
|
@@ -35,7 +37,15 @@ A HOME can hold several Codex homes, and `codexReader.locate` reads all of them
|
|
|
35
37
|
|
|
36
38
|
Homes that resolve to one directory count once. A session copied into several homes (`acct move`) is one ref, its largest copy. A lookup by id takes the rollout whose name ends in the id; when no name does, it reads the `session_meta` of each rollout modified since the id's UUIDv7 time.
|
|
37
39
|
|
|
38
|
-
|
|
40
|
+
`listReaders()` and the exported catalog own the complete installed format inventory.
|
|
41
|
+
|
|
42
|
+
## Child execution evidence
|
|
43
|
+
|
|
44
|
+
Kimi records a child `agent_id` and `parent_tool_call_id` on each wrapped `SubagentEvent`, and writes the child's own wire under the parent's `subagents/<agent_id>/` directory. `kimiReader.locate(home, { nativeSessionId: parentId })` returns the parent and these child refs. A child-ID lookup selects that child. The reader keeps wrapped child events out of the parent's messages and usage; read each returned child ref for its own recorded work. `kimiRefForFile` preserves the same parent binding when a child wire is read directly. A missing child wire remains missing execution evidence, even when its ID appears in the parent.
|
|
45
|
+
|
|
46
|
+
Pi's session-header `parentSession` is fork/clone/handoff lineage; it does not prove a delegated subagent. The native reader does not turn it or conversation-entry `parentId` into subagent execution. Extension workflow execution remains a separate recorded source. Factory Droid also needs an explicit producer-recorded parent/child binding before this reader can expose delegated children. Neither reader guesses relationships from a tool name, working directory, timing, or current project.
|
|
47
|
+
|
|
48
|
+
A bounded local capture inspection on 2026-10-25 found explicit Kimi child events and separate child wires. Its Pi and Factory Droid samples contained no explicit subagent binding: the latest 20 files per harness held 7,724 and 245 records respectively; an additional size-stratified selection read seven Pi files (8,229 records) and 20 Factory files (252 records). These are sample limits, not a claim that every producer version lacks the metadata. The remaining Pi/Factory child coverage is unknown. Closing it requires a retained parent/child capture with an explicit source binding, followed by a reader-to-OTLP integration proof; the graph-corpus integrating owner owns that follow-up.
|
|
39
49
|
|
|
40
50
|
## Use
|
|
41
51
|
|
|
@@ -77,5 +87,5 @@ harness-sessions read <file|home|capture> [--harness h] [--session id] [--summar
|
|
|
77
87
|
|
|
78
88
|
## Tests
|
|
79
89
|
|
|
80
|
-
The fixtures are sessions that the real harness CLIs wrote while talking to the trace-proof scripted model; the tests assert that script.
|
|
90
|
+
The baseline fixtures are sessions that the real harness CLIs wrote while talking to the trace-proof scripted model; the tests assert that script. The Kimi child fixture retains complete existing parent and child wire records with all content removed, IDs pseudonymized, and source hashes, record counts, redaction version and token counters recorded in `test/fixtures/kimi/provenance.json`. Its integration test exercises discovery, full/summary reads, in-memory input and contract-span projection, preserving parent and child usage separately.
|
|
81
91
|
The proof against the readers this package replaces is a comparison over stored sessions, recorded with each release.
|
package/dist/formats/kimi.js
CHANGED
|
@@ -21,6 +21,13 @@ const STORE = STORES[FORMAT];
|
|
|
21
21
|
const HARNESS = 'kimi-code';
|
|
22
22
|
/** The event stream this reader folds; the store also holds context.jsonl and state.json. */
|
|
23
23
|
const WIRE = '*/{id}/wire.jsonl';
|
|
24
|
+
const CHILD_WIRE = '*/*/subagents/{id}/wire.jsonl';
|
|
25
|
+
/** Kimi's producer places each agent's wire under its owning session's subagents directory. */
|
|
26
|
+
function sessionIdentity(path) {
|
|
27
|
+
const directory = dirname(path);
|
|
28
|
+
const container = dirname(directory);
|
|
29
|
+
return { id: basename(directory), parent: basename(container) === 'subagents' ? basename(dirname(container)) : null };
|
|
30
|
+
}
|
|
24
31
|
function usageOf(value) {
|
|
25
32
|
if (!isRecord(value))
|
|
26
33
|
return null;
|
|
@@ -142,7 +149,7 @@ export function createFold(ref, mode, stats) {
|
|
|
142
149
|
subagentEvents += 1;
|
|
143
150
|
const agent = str(payload.agent_id);
|
|
144
151
|
if (agent)
|
|
145
|
-
builder.child(agent, str(payload.parent_tool_call_id));
|
|
152
|
+
builder.child(agent, str(payload.parent_tool_call_id) ?? str(payload.task_tool_call_id));
|
|
146
153
|
break;
|
|
147
154
|
}
|
|
148
155
|
default:
|
|
@@ -155,7 +162,7 @@ export function createFold(ref, mode, stats) {
|
|
|
155
162
|
if (compactions > 0)
|
|
156
163
|
builder.gap(`${compactions} compactions replaced earlier context`);
|
|
157
164
|
if (subagentEvents > 0)
|
|
158
|
-
builder.gap(`${subagentEvents} subagent events
|
|
165
|
+
builder.gap(`${subagentEvents} wrapped subagent events were excluded from this session's own messages and usage; separate child wires must be read for child execution`);
|
|
159
166
|
builder.inferEnding(aborted ? { at: aborted.at, error: null } : undefined);
|
|
160
167
|
return builder;
|
|
161
168
|
};
|
|
@@ -166,10 +173,15 @@ export async function fold(ref, mode, options, source = fileRecords(ref.path, {
|
|
|
166
173
|
}
|
|
167
174
|
async function locate(home, opts) {
|
|
168
175
|
const root = join(home, STORE.root);
|
|
169
|
-
const pattern = storeGlob(WIRE
|
|
176
|
+
const pattern = storeGlob(WIRE);
|
|
177
|
+
const children = storeGlob(CHILD_WIRE);
|
|
170
178
|
const refs = [];
|
|
171
179
|
for (const rel of await walkFiles(root)) {
|
|
172
|
-
if (!pattern.test(rel))
|
|
180
|
+
if (!pattern.test(rel) && !children.test(rel))
|
|
181
|
+
continue;
|
|
182
|
+
const { id, parent } = sessionIdentity(rel);
|
|
183
|
+
// A parent lookup includes its recorded child wires; a child lookup selects that agent.
|
|
184
|
+
if (opts.nativeSessionId && id !== opts.nativeSessionId && parent !== opts.nativeSessionId)
|
|
173
185
|
continue;
|
|
174
186
|
const path = join(root, rel);
|
|
175
187
|
const mtime = await mtimeMs(path);
|
|
@@ -178,10 +190,9 @@ async function locate(home, opts) {
|
|
|
178
190
|
// Kimi keys the session directory by a hash of its working directory and records no cwd.
|
|
179
191
|
if (opts.cwd)
|
|
180
192
|
continue;
|
|
181
|
-
const id = basename(dirname(path));
|
|
182
193
|
const context = join(dirname(path), 'context.jsonl');
|
|
183
194
|
const files = (await mtimeMs(context)) > 0 ? [path, context] : [path];
|
|
184
|
-
refs.push({ harness: HARNESS, format: FORMAT, nativeSessionId: id, path, files, home, cwd: null, mtimeMs: mtime, parentNativeSessionId:
|
|
195
|
+
refs.push({ harness: HARNESS, format: FORMAT, nativeSessionId: id, path, files, home, cwd: null, mtimeMs: mtime, parentNativeSessionId: parent });
|
|
185
196
|
}
|
|
186
197
|
return refs.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
187
198
|
}
|
|
@@ -190,7 +201,7 @@ export const kimiReader = {
|
|
|
190
201
|
aliases: ['kimi', 'kimi-cli'],
|
|
191
202
|
formats: [FORMAT],
|
|
192
203
|
stores: [STORE],
|
|
193
|
-
globs: { session: WIRE },
|
|
204
|
+
globs: { session: WIRE, children: CHILD_WIRE },
|
|
194
205
|
evidence: EVIDENCE[FORMAT],
|
|
195
206
|
locate: (home, opts = {}) => locate(home, opts),
|
|
196
207
|
async read(ref, opts = {}) {
|
|
@@ -202,5 +213,6 @@ export const kimiReader = {
|
|
|
202
213
|
};
|
|
203
214
|
/** A session read from its `wire.jsonl` outside a HOME layout. */
|
|
204
215
|
export function kimiRefForFile(path) {
|
|
205
|
-
|
|
216
|
+
const { id, parent } = sessionIdentity(path);
|
|
217
|
+
return { harness: HARNESS, format: FORMAT, nativeSessionId: id, path, files: [path], home: null, cwd: null, mtimeMs: 0, parentNativeSessionId: parent };
|
|
206
218
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/harness-sessions",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "One reader for every coding-agent harness's native session (Claude Code, Codex, OpenCode, Pi): store layouts, a normalized session with messages, tool calls, served models, usage and ending errors, and projections to chat messages, OTLP spans and turns",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|