@omnitype-code/adapter-sdk 2.0.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.
- package/README.md +163 -0
- package/dist/adapters/filesystem-only.d.ts +30 -0
- package/dist/adapters/filesystem-only.d.ts.map +1 -0
- package/dist/adapters/filesystem-only.js +115 -0
- package/dist/adapters/filesystem-only.js.map +1 -0
- package/dist/adapters/hook-poller.d.ts +40 -0
- package/dist/adapters/hook-poller.d.ts.map +1 -0
- package/dist/adapters/hook-poller.js +125 -0
- package/dist/adapters/hook-poller.js.map +1 -0
- package/dist/adapters/index.d.ts +34 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +61 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/stdin-json-hook-tool.d.ts +87 -0
- package/dist/adapters/stdin-json-hook-tool.d.ts.map +1 -0
- package/dist/adapters/stdin-json-hook-tool.js +491 -0
- package/dist/adapters/stdin-json-hook-tool.js.map +1 -0
- package/dist/adapters/transcript-scavenger.d.ts +40 -0
- package/dist/adapters/transcript-scavenger.d.ts.map +1 -0
- package/dist/adapters/transcript-scavenger.js +569 -0
- package/dist/adapters/transcript-scavenger.js.map +1 -0
- package/dist/classifier.d.ts +132 -0
- package/dist/classifier.d.ts.map +1 -0
- package/dist/classifier.js +95 -0
- package/dist/classifier.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/loader.d.ts +51 -0
- package/dist/loader.d.ts.map +1 -0
- package/dist/loader.js +352 -0
- package/dist/loader.js.map +1 -0
- package/dist/manifest.d.ts +131 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +12 -0
- package/dist/manifest.js.map +1 -0
- package/dist/normalize.d.ts +51 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/normalize.js +122 -0
- package/dist/normalize.js.map +1 -0
- package/dist/tier.d.ts +12 -0
- package/dist/tier.d.ts.map +1 -0
- package/dist/tier.js +62 -0
- package/dist/tier.js.map +1 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# @omnitype-code/adapter-sdk
|
|
2
|
+
|
|
3
|
+
The adapter layer for OmniType v2 deterministic attribution.
|
|
4
|
+
|
|
5
|
+
This SDK is for **AI tool authors** and **open-source contributors** who want to add OmniType attribution support to a coding tool (Cursor, Windsurf, Cline, a custom CLI, etc.).
|
|
6
|
+
|
|
7
|
+
If you are an AI tool **end user** looking to track attribution in your own workflow, install the VSCode extension instead.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## How the tier system works
|
|
12
|
+
|
|
13
|
+
Every file edit tracked by OmniType gets an attribution **tier**:
|
|
14
|
+
|
|
15
|
+
| Tier | Name | How it's produced | Confidence |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| **T0** | Deterministic | AI tool embeds `@omnitype-code/agent-sdk` directly | 100% |
|
|
18
|
+
| **T1** | Hook-inferred | `preToolUse` hook fires before each tool call | 85% |
|
|
19
|
+
| **T2** | Retrospective | Transcript scavenger reads session logs after the fact | 60% |
|
|
20
|
+
| **T3** | Filesystem-only | File watcher sees a change with no session context | 30% |
|
|
21
|
+
|
|
22
|
+
The goal is to get as much attribution as possible at T0 or T1. T3 spans have `origin='unknown'` — they are **never** assumed to be AI.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Option A — Embed the Agent SDK (T0 path)
|
|
27
|
+
|
|
28
|
+
Use `@omnitype-code/agent-sdk` if your tool writes files directly (not via shell hooks).
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @omnitype-code/agent-sdk
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { OmniTypeAgent } from '@omnitype-code/agent-sdk';
|
|
36
|
+
|
|
37
|
+
// On session start (once per AI conversation)
|
|
38
|
+
const agent = await OmniTypeAgent.tryConnect({
|
|
39
|
+
workspace: '/path/to/git/repo',
|
|
40
|
+
sessionId: crypto.randomUUID(),
|
|
41
|
+
model: 'claude-sonnet-4-6',
|
|
42
|
+
tool: 'my-tool',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Wrap every file write — produces T0 (deterministic) attribution
|
|
46
|
+
if (agent) {
|
|
47
|
+
await agent.withEdit('src/foo.ts', newContent, async (absPath, content) => {
|
|
48
|
+
await fs.writeFile(absPath, content, 'utf-8');
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
// Daemon not running — write normally, attribution will be T3
|
|
52
|
+
await fs.writeFile(absPath, newContent, 'utf-8');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// On session end
|
|
56
|
+
await agent?.end('tool-exit');
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`tryConnect` returns `null` if the daemon isn't running — your tool continues working normally and writes are tracked at T3 by the filesystem watcher.
|
|
60
|
+
|
|
61
|
+
### Fine-grained API
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// Manual txn control (for multi-file edits)
|
|
65
|
+
const txn = await agent.beginTxn(['src/a.ts', 'src/b.ts']);
|
|
66
|
+
await writeA();
|
|
67
|
+
await writeB();
|
|
68
|
+
await agent.commitTxn(txn, [
|
|
69
|
+
{ path: 'src/a.ts', preHash, postHash, splices },
|
|
70
|
+
{ path: 'src/b.ts', preHash, postHash, splices },
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
// Emit the prompt (privacy-safe — only hash stored by default)
|
|
74
|
+
await agent.emitPrompt(promptText, /* shareText= */ false);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Option B — Write a Manifest (T1 path)
|
|
80
|
+
|
|
81
|
+
If your tool runs shell commands that can be intercepted via hooks, write a 30-line JSON manifest.
|
|
82
|
+
|
|
83
|
+
Create `.omnitype/adapters/my-tool/manifest.json`:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"id": "my-tool",
|
|
88
|
+
"version": "1.0.0",
|
|
89
|
+
"display_name": "My AI Tool",
|
|
90
|
+
"tool_binary": "my-tool",
|
|
91
|
+
"trust": { "trust_circle": 2 },
|
|
92
|
+
"declared_capabilities": [
|
|
93
|
+
"tool.identity",
|
|
94
|
+
"session.id",
|
|
95
|
+
"hook.preToolUse",
|
|
96
|
+
"operation.editOp.path_only"
|
|
97
|
+
],
|
|
98
|
+
"hooks": {
|
|
99
|
+
"preToolUse": {
|
|
100
|
+
"install_path": "~/.config/my-tool/hooks/preToolUse.sh",
|
|
101
|
+
"one_liner": "echo '{\"type\":\"hook\",\"tool\":\"my-tool\",\"session\":\"$SESSION_ID\",\"file\":\"$FILE\"}' >> ${OMNITYPE_HOOKS_DIR}/event-$(date +%s%N).json"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
"normalize": [
|
|
105
|
+
{
|
|
106
|
+
"match": { "tool_name": "write_file" },
|
|
107
|
+
"emit": "EditOp",
|
|
108
|
+
"path_from": "$.arguments.path",
|
|
109
|
+
"origin": "ai"
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Then run:
|
|
116
|
+
```bash
|
|
117
|
+
omnitype-daemon install-hooks
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The daemon will install the hook and start receiving T1 events from your tool.
|
|
121
|
+
|
|
122
|
+
### Trust circles
|
|
123
|
+
|
|
124
|
+
| Circle | Level | Who uses it |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| 1 | Verified partner | Claude Code, Cursor, Windsurf (manifests maintained by OmniType) |
|
|
127
|
+
| 2 | Community verified | Cline, Copilot (community-maintained, OmniType-reviewed) |
|
|
128
|
+
| 3 | Community | Unreviewed third-party manifests |
|
|
129
|
+
|
|
130
|
+
T0 is only achievable via the Agent SDK. Community (circle 3) adapters are capped at T1.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## The attribution guarantee
|
|
135
|
+
|
|
136
|
+
**OmniType v2 never assumes an unknown write is AI-generated.**
|
|
137
|
+
|
|
138
|
+
- `origin='unknown'` means: a file changed, but we don't know who wrote it.
|
|
139
|
+
- `origin='ai'` requires positive evidence: an active AI session txn, a signed Agent SDK event, or a hook payload.
|
|
140
|
+
- This is enforced in the SQLite materializer — there is no code path that promotes `'unknown'` to `'ai'`.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Verifying your adapter
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
# Check your adapter is installed and recognised
|
|
148
|
+
omnitype-daemon doctor
|
|
149
|
+
|
|
150
|
+
# See live attribution as you code
|
|
151
|
+
omnitype-daemon blame src/foo.ts
|
|
152
|
+
|
|
153
|
+
# Check tier breakdown for the workspace
|
|
154
|
+
omnitype-daemon health
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Filing issues
|
|
160
|
+
|
|
161
|
+
Open an issue at [github.com/omnitype-code/omnitype-v2](https://github.com/omnitype-code/omnitype-v2) with the label `adapter`.
|
|
162
|
+
|
|
163
|
+
Include the output of `omnitype-daemon doctor` and `omnitype-daemon health --json`.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* filesystem-only-adapter — always loaded, cannot be disabled.
|
|
3
|
+
*
|
|
4
|
+
* Watches files for changes that weren't recorded by any other adapter,
|
|
5
|
+
* computes a Myers diff, and emits UnattributedDelta with confidence < 1.0.
|
|
6
|
+
*
|
|
7
|
+
* Key invariants:
|
|
8
|
+
* - NEVER emits origin='ai'. Unknown writers are 'unknown'.
|
|
9
|
+
* - Always tier T3.
|
|
10
|
+
* - Only emits for files that have an existing FileOpened record (tracked).
|
|
11
|
+
* - Deduplicates against changes already recorded by higher-tier adapters
|
|
12
|
+
* (if post_hash matches materialized hash, skip).
|
|
13
|
+
*/
|
|
14
|
+
import type { EmitFn } from '../loader.js';
|
|
15
|
+
export declare const ADAPTER_ID = "org.omnitype.adapters.filesystem-only";
|
|
16
|
+
export declare class FilesystemOnlyAdapter {
|
|
17
|
+
private watched;
|
|
18
|
+
private watchers;
|
|
19
|
+
private emit;
|
|
20
|
+
private workspace;
|
|
21
|
+
private actor;
|
|
22
|
+
constructor(emit: EmitFn, workspace: string, user: string, host: string);
|
|
23
|
+
/** Called by the materializer when a file is first opened/tracked */
|
|
24
|
+
trackFile(relPath: string, currentHash: string): void;
|
|
25
|
+
untrackFile(relPath: string): void;
|
|
26
|
+
private onFileChange;
|
|
27
|
+
private guessCandidates;
|
|
28
|
+
stopAll(): void;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=filesystem-only.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem-only.d.ts","sourceRoot":"","sources":["../../src/adapters/filesystem-only.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,eAAO,MAAM,UAAU,0CAA0C,CAAC;AAQlE,qBAAa,qBAAqB;IAChC,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,QAAQ,CAA+C;IAC/D,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,KAAK,CAAQ;gBAET,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAYvE,qEAAqE;IACrE,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAarD,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAMlC,OAAO,CAAC,YAAY;IAiCpB,OAAO,CAAC,eAAe;IAMvB,OAAO,IAAI,IAAI;CAKhB"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* filesystem-only-adapter — always loaded, cannot be disabled.
|
|
3
|
+
*
|
|
4
|
+
* Watches files for changes that weren't recorded by any other adapter,
|
|
5
|
+
* computes a Myers diff, and emits UnattributedDelta with confidence < 1.0.
|
|
6
|
+
*
|
|
7
|
+
* Key invariants:
|
|
8
|
+
* - NEVER emits origin='ai'. Unknown writers are 'unknown'.
|
|
9
|
+
* - Always tier T3.
|
|
10
|
+
* - Only emits for files that have an existing FileOpened record (tracked).
|
|
11
|
+
* - Deduplicates against changes already recorded by higher-tier adapters
|
|
12
|
+
* (if post_hash matches materialized hash, skip).
|
|
13
|
+
*/
|
|
14
|
+
import { watch, readFileSync, statSync } from 'node:fs';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { hashBytes } from '@omnitype-code/journal';
|
|
17
|
+
export const ADAPTER_ID = 'org.omnitype.adapters.filesystem-only';
|
|
18
|
+
export class FilesystemOnlyAdapter {
|
|
19
|
+
watched = new Map();
|
|
20
|
+
watchers = new Map();
|
|
21
|
+
emit;
|
|
22
|
+
workspace;
|
|
23
|
+
actor;
|
|
24
|
+
constructor(emit, workspace, user, host) {
|
|
25
|
+
this.emit = emit;
|
|
26
|
+
this.workspace = workspace;
|
|
27
|
+
this.actor = {
|
|
28
|
+
kind: 'unknown',
|
|
29
|
+
tool: 'unknown',
|
|
30
|
+
user,
|
|
31
|
+
host,
|
|
32
|
+
workspace,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Called by the materializer when a file is first opened/tracked */
|
|
36
|
+
trackFile(relPath, currentHash) {
|
|
37
|
+
const absPath = join(this.workspace, relPath);
|
|
38
|
+
this.watched.set(relPath, { path: relPath, absPath, lastHash: currentHash });
|
|
39
|
+
if (this.watchers.has(relPath))
|
|
40
|
+
return;
|
|
41
|
+
const watcher = watch(absPath, { persistent: false }, () => {
|
|
42
|
+
// Debounce 150ms
|
|
43
|
+
setTimeout(() => this.onFileChange(relPath), 150);
|
|
44
|
+
});
|
|
45
|
+
this.watchers.set(relPath, watcher);
|
|
46
|
+
}
|
|
47
|
+
untrackFile(relPath) {
|
|
48
|
+
this.watchers.get(relPath)?.close();
|
|
49
|
+
this.watchers.delete(relPath);
|
|
50
|
+
this.watched.delete(relPath);
|
|
51
|
+
}
|
|
52
|
+
onFileChange(relPath) {
|
|
53
|
+
const entry = this.watched.get(relPath);
|
|
54
|
+
if (!entry)
|
|
55
|
+
return;
|
|
56
|
+
let newContent;
|
|
57
|
+
try {
|
|
58
|
+
newContent = readFileSync(entry.absPath, 'utf-8');
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return; // file deleted or unreadable
|
|
62
|
+
}
|
|
63
|
+
const newHash = hashBytes(Buffer.from(newContent, 'utf-8'));
|
|
64
|
+
if (newHash === entry.lastHash)
|
|
65
|
+
return; // no real change
|
|
66
|
+
const oldHash = entry.lastHash;
|
|
67
|
+
entry.lastHash = newHash;
|
|
68
|
+
const ops = computeSimpleDiff(entry.absPath, oldHash, newContent);
|
|
69
|
+
if (ops.length === 0)
|
|
70
|
+
return;
|
|
71
|
+
const body = {
|
|
72
|
+
type: 'UnattributedDelta',
|
|
73
|
+
path: relPath,
|
|
74
|
+
pre_hash: oldHash,
|
|
75
|
+
post_hash: newHash,
|
|
76
|
+
ops,
|
|
77
|
+
candidates: this.guessCandidates(relPath),
|
|
78
|
+
confidence: 0.0, // explicitly zero — we don't know who did this
|
|
79
|
+
};
|
|
80
|
+
this.emit(this.actor, body, 'T3', ADAPTER_ID);
|
|
81
|
+
}
|
|
82
|
+
guessCandidates(relPath) {
|
|
83
|
+
// In the future: query process-observation-adapter here
|
|
84
|
+
// For now: empty candidate list (honest unknown)
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
stopAll() {
|
|
88
|
+
for (const w of this.watchers.values())
|
|
89
|
+
w.close();
|
|
90
|
+
this.watchers.clear();
|
|
91
|
+
this.watched.clear();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// ─── Simple diff: whole-file replace represented as one splice ────────────────
|
|
95
|
+
// This is the v1 LCS downgraded to its honest role: T3, UnattributedDelta only.
|
|
96
|
+
// A real Myers diff is a future improvement; correctness of attribution doesn't
|
|
97
|
+
// depend on diff quality here since everything is already tagged T3/confidence=0.
|
|
98
|
+
function computeSimpleDiff(absPath, oldHash, newContent) {
|
|
99
|
+
// Represent as a full-file replacement splice.
|
|
100
|
+
// Byte-accurate diff can be added later; for now we model it as:
|
|
101
|
+
// delete everything, insert new content.
|
|
102
|
+
let oldLen = 0;
|
|
103
|
+
try {
|
|
104
|
+
oldLen = statSync(absPath).size;
|
|
105
|
+
}
|
|
106
|
+
catch { /* file might be new */ }
|
|
107
|
+
return [
|
|
108
|
+
{
|
|
109
|
+
offset: 0,
|
|
110
|
+
delete_len: oldLen,
|
|
111
|
+
insert_text: newContent,
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=filesystem-only.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem-only.js","sourceRoot":"","sources":["../../src/adapters/filesystem-only.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hook-poller — polls the .omnitype/hooks/ directory for events written
|
|
3
|
+
* by hook one-liners and converts them to journal events.
|
|
4
|
+
*
|
|
5
|
+
* Each tool's preToolUse hook writes a JSON file to the pipeDir.
|
|
6
|
+
* This poller drains those files every POLL_INTERVAL_MS and emits:
|
|
7
|
+
* - SessionStarted (if session_id is new)
|
|
8
|
+
* - ModelClaim (if model is present)
|
|
9
|
+
* - TxnBegin (for write-type tool calls)
|
|
10
|
+
* - The actual EditOp comes later from the VSCode discriminator or
|
|
11
|
+
* the filesystem watcher — NOT from the hook itself.
|
|
12
|
+
*
|
|
13
|
+
* This replaces the v1 sentinel approach.
|
|
14
|
+
* Key improvements over v1:
|
|
15
|
+
* - Events are consumed and deleted — no stale TTL races.
|
|
16
|
+
* - Session is tracked explicitly — no 30s window heuristics.
|
|
17
|
+
* - Model comes from actual hook payload — not terminal scraping.
|
|
18
|
+
* - process.cwd() issue is avoided: we know the workspace because
|
|
19
|
+
* the daemon already owns the workspace context.
|
|
20
|
+
*/
|
|
21
|
+
import type { EmitFn } from '../loader.js';
|
|
22
|
+
export declare const ADAPTER_ID = "org.omnitype.adapters.hook-poller";
|
|
23
|
+
export declare class HookPoller {
|
|
24
|
+
private pipeDir;
|
|
25
|
+
private emit;
|
|
26
|
+
private workspace;
|
|
27
|
+
private user;
|
|
28
|
+
private host;
|
|
29
|
+
private timer;
|
|
30
|
+
private seenSessions;
|
|
31
|
+
private openTxns;
|
|
32
|
+
constructor(emit: EmitFn, workspace: string, user: string, host: string);
|
|
33
|
+
start(): void;
|
|
34
|
+
private poll;
|
|
35
|
+
private processHookEvent;
|
|
36
|
+
getOpenTxn(sessionId: string): string | undefined;
|
|
37
|
+
closeOpenTxn(sessionId: string): void;
|
|
38
|
+
stop(): void;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=hook-poller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-poller.d.ts","sourceRoot":"","sources":["../../src/adapters/hook-poller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAUH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAQ3C,eAAO,MAAM,UAAU,sCAAsC,CAAC;AAI9D,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,YAAY,CAAqB;IAEzC,OAAO,CAAC,QAAQ,CAA6B;gBAEjC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAQvE,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,IAAI;IAOZ,OAAO,CAAC,gBAAgB;IAgExB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIjD,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC,IAAI,IAAI,IAAI;CAMb"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hook-poller — polls the .omnitype/hooks/ directory for events written
|
|
3
|
+
* by hook one-liners and converts them to journal events.
|
|
4
|
+
*
|
|
5
|
+
* Each tool's preToolUse hook writes a JSON file to the pipeDir.
|
|
6
|
+
* This poller drains those files every POLL_INTERVAL_MS and emits:
|
|
7
|
+
* - SessionStarted (if session_id is new)
|
|
8
|
+
* - ModelClaim (if model is present)
|
|
9
|
+
* - TxnBegin (for write-type tool calls)
|
|
10
|
+
* - The actual EditOp comes later from the VSCode discriminator or
|
|
11
|
+
* the filesystem watcher — NOT from the hook itself.
|
|
12
|
+
*
|
|
13
|
+
* This replaces the v1 sentinel approach.
|
|
14
|
+
* Key improvements over v1:
|
|
15
|
+
* - Events are consumed and deleted — no stale TTL races.
|
|
16
|
+
* - Session is tracked explicitly — no 30s window heuristics.
|
|
17
|
+
* - Model comes from actual hook payload — not terminal scraping.
|
|
18
|
+
* - process.cwd() issue is avoided: we know the workspace because
|
|
19
|
+
* the daemon already owns the workspace context.
|
|
20
|
+
*/
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
23
|
+
import { drainHookEvents } from './stdin-json-hook-tool.js';
|
|
24
|
+
import { isWriteToolCall, hookPayloadToFilePath, hookPayloadToSessionEvent, } from '../normalize.js';
|
|
25
|
+
export const ADAPTER_ID = 'org.omnitype.adapters.hook-poller';
|
|
26
|
+
const POLL_INTERVAL_MS = 100;
|
|
27
|
+
const HOOK_TIER = 'T1'; // hook + workspace context = T1
|
|
28
|
+
export class HookPoller {
|
|
29
|
+
pipeDir;
|
|
30
|
+
emit;
|
|
31
|
+
workspace;
|
|
32
|
+
user;
|
|
33
|
+
host;
|
|
34
|
+
timer = null;
|
|
35
|
+
seenSessions = new Set();
|
|
36
|
+
// Maps session_id → open txn_id (for deduplication)
|
|
37
|
+
openTxns = new Map();
|
|
38
|
+
constructor(emit, workspace, user, host) {
|
|
39
|
+
this.pipeDir = join(workspace, '.omnitype', 'hooks');
|
|
40
|
+
this.emit = emit;
|
|
41
|
+
this.workspace = workspace;
|
|
42
|
+
this.user = user;
|
|
43
|
+
this.host = host;
|
|
44
|
+
}
|
|
45
|
+
start() {
|
|
46
|
+
this.timer = setInterval(() => this.poll(), POLL_INTERVAL_MS);
|
|
47
|
+
this.timer.unref();
|
|
48
|
+
}
|
|
49
|
+
poll() {
|
|
50
|
+
const events = drainHookEvents(this.pipeDir);
|
|
51
|
+
for (const event of events) {
|
|
52
|
+
this.processHookEvent(event);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
processHookEvent(event) {
|
|
56
|
+
const { tool, payload, ts } = event;
|
|
57
|
+
const sessionInfo = hookPayloadToSessionEvent(payload, tool);
|
|
58
|
+
const filePath = hookPayloadToFilePath(payload);
|
|
59
|
+
// Antigravity uses toolCall.name; legacy tools use tool_name
|
|
60
|
+
const toolName = payload.tool_name ?? payload.toolCall?.name;
|
|
61
|
+
const actor = {
|
|
62
|
+
kind: 'ai',
|
|
63
|
+
tool,
|
|
64
|
+
model: payload.model ?? sessionInfo?.model ?? 'unknown',
|
|
65
|
+
session_id: sessionInfo?.session_id,
|
|
66
|
+
user: this.user,
|
|
67
|
+
host: this.host,
|
|
68
|
+
workspace: this.workspace,
|
|
69
|
+
};
|
|
70
|
+
// Emit SessionStarted (once per session_id)
|
|
71
|
+
if (sessionInfo?.session_id && !this.seenSessions.has(sessionInfo.session_id)) {
|
|
72
|
+
this.seenSessions.add(sessionInfo.session_id);
|
|
73
|
+
const dummyPromptHash = createHash('sha256').update(sessionInfo.session_id).digest('hex');
|
|
74
|
+
const sessionEvent = {
|
|
75
|
+
type: 'SessionStarted',
|
|
76
|
+
session_id: sessionInfo.session_id,
|
|
77
|
+
tool,
|
|
78
|
+
model: sessionInfo.model || 'unknown',
|
|
79
|
+
prompt_hash: dummyPromptHash,
|
|
80
|
+
prompt_bytes: 0,
|
|
81
|
+
};
|
|
82
|
+
this.emit(actor, sessionEvent, HOOK_TIER, ADAPTER_ID);
|
|
83
|
+
}
|
|
84
|
+
// Emit ModelClaim if model present
|
|
85
|
+
if (payload.model && sessionInfo?.session_id) {
|
|
86
|
+
const modelEvent = {
|
|
87
|
+
type: 'ModelClaim',
|
|
88
|
+
session_id: sessionInfo.session_id,
|
|
89
|
+
model: payload.model,
|
|
90
|
+
source: 'hook-stdin',
|
|
91
|
+
};
|
|
92
|
+
this.emit(actor, modelEvent, HOOK_TIER, ADAPTER_ID);
|
|
93
|
+
}
|
|
94
|
+
// Emit TxnBegin for write-type tool calls
|
|
95
|
+
if (isWriteToolCall(toolName) && sessionInfo?.session_id) {
|
|
96
|
+
const existingTxn = this.openTxns.get(sessionInfo.session_id);
|
|
97
|
+
if (!existingTxn) {
|
|
98
|
+
const txnId = randomBytes(16).toString('hex');
|
|
99
|
+
this.openTxns.set(sessionInfo.session_id, txnId);
|
|
100
|
+
const txnBegin = {
|
|
101
|
+
type: 'TxnBegin',
|
|
102
|
+
txn_id: txnId,
|
|
103
|
+
session_id: sessionInfo.session_id,
|
|
104
|
+
expected_files: filePath ? [filePath] : [],
|
|
105
|
+
};
|
|
106
|
+
this.emit(actor, txnBegin, HOOK_TIER, ADAPTER_ID);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// Note: TxnCommit is emitted by the VSCode extension or watcher
|
|
110
|
+
// when the actual file change is observed and linked to this txn.
|
|
111
|
+
}
|
|
112
|
+
getOpenTxn(sessionId) {
|
|
113
|
+
return this.openTxns.get(sessionId);
|
|
114
|
+
}
|
|
115
|
+
closeOpenTxn(sessionId) {
|
|
116
|
+
this.openTxns.delete(sessionId);
|
|
117
|
+
}
|
|
118
|
+
stop() {
|
|
119
|
+
if (this.timer) {
|
|
120
|
+
clearInterval(this.timer);
|
|
121
|
+
this.timer = null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=hook-poller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hook-poller.js","sourceRoot":"","sources":["../../src/adapters/hook-poller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in adapter suite.
|
|
3
|
+
*
|
|
4
|
+
* Production guarantees:
|
|
5
|
+
* - Any individual adapter failing to start does NOT abort the daemon.
|
|
6
|
+
* - The suite always returns a usable handle, even when every adapter fails
|
|
7
|
+
* (the FilesystemOnlyAdapter is the always-on graceful-degradation path).
|
|
8
|
+
* - Each adapter's health is reported through `getHealth()`, so the daemon
|
|
9
|
+
* can expose it via `query_health` and the dashboard can show users
|
|
10
|
+
* which tools fell back to T3.
|
|
11
|
+
*/
|
|
12
|
+
import { FilesystemOnlyAdapter } from './filesystem-only.js';
|
|
13
|
+
import { HookPoller } from './hook-poller.js';
|
|
14
|
+
import { TranscriptScavengerAdapter } from './transcript-scavenger.js';
|
|
15
|
+
import type { EmitFn } from '../loader.js';
|
|
16
|
+
export interface AdapterHealth {
|
|
17
|
+
id: string;
|
|
18
|
+
started: boolean;
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface AdapterSuite {
|
|
22
|
+
filesystemOnly: FilesystemOnlyAdapter;
|
|
23
|
+
hookPoller: HookPoller | null;
|
|
24
|
+
transcriptScavenger: TranscriptScavengerAdapter | null;
|
|
25
|
+
getHealth(): AdapterHealth[];
|
|
26
|
+
stop(): void;
|
|
27
|
+
}
|
|
28
|
+
export declare function startBuiltinAdapters(emit: EmitFn, workspace: string, user: string, host: string): AdapterSuite;
|
|
29
|
+
export { FilesystemOnlyAdapter } from './filesystem-only.js';
|
|
30
|
+
export { HookPoller } from './hook-poller.js';
|
|
31
|
+
export { TranscriptScavengerAdapter } from './transcript-scavenger.js';
|
|
32
|
+
export { installHooks, TOOL_SPECS, buildHookOneliner } from './stdin-json-hook-tool.js';
|
|
33
|
+
export type { InstallResult, ToolHookSpec } from './stdin-json-hook-tool.js';
|
|
34
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3C,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,cAAc,EAAE,qBAAqB,CAAC;IACtC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,mBAAmB,EAAE,0BAA0B,GAAG,IAAI,CAAC;IACvD,SAAS,IAAI,aAAa,EAAE,CAAC;IAC7B,IAAI,IAAI,IAAI,CAAC;CACd;AAmBD,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,YAAY,CA+Bd;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACxF,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in adapter suite.
|
|
3
|
+
*
|
|
4
|
+
* Production guarantees:
|
|
5
|
+
* - Any individual adapter failing to start does NOT abort the daemon.
|
|
6
|
+
* - The suite always returns a usable handle, even when every adapter fails
|
|
7
|
+
* (the FilesystemOnlyAdapter is the always-on graceful-degradation path).
|
|
8
|
+
* - Each adapter's health is reported through `getHealth()`, so the daemon
|
|
9
|
+
* can expose it via `query_health` and the dashboard can show users
|
|
10
|
+
* which tools fell back to T3.
|
|
11
|
+
*/
|
|
12
|
+
import { FilesystemOnlyAdapter } from './filesystem-only.js';
|
|
13
|
+
import { HookPoller } from './hook-poller.js';
|
|
14
|
+
import { TranscriptScavengerAdapter } from './transcript-scavenger.js';
|
|
15
|
+
function safeStart(adapter, id, health) {
|
|
16
|
+
try {
|
|
17
|
+
adapter.start();
|
|
18
|
+
health.push({ id, started: true });
|
|
19
|
+
return adapter;
|
|
20
|
+
}
|
|
21
|
+
catch (err) {
|
|
22
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
23
|
+
console.error(`[omnitype-adapters] ${id} failed to start: ${msg}`);
|
|
24
|
+
health.push({ id, started: false, error: msg });
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function startBuiltinAdapters(emit, workspace, user, host) {
|
|
29
|
+
const health = [];
|
|
30
|
+
// FilesystemOnlyAdapter is always-on, started per-file via trackFile().
|
|
31
|
+
// It is the universal fallback: any unsupported tool's writes are caught here.
|
|
32
|
+
const filesystemOnly = new FilesystemOnlyAdapter(emit, workspace, user, host);
|
|
33
|
+
health.push({ id: 'org.omnitype.adapters.filesystem-only', started: true });
|
|
34
|
+
const hookPoller = safeStart(new HookPoller(emit, workspace, user, host), 'org.omnitype.adapters.hook-poller', health);
|
|
35
|
+
const transcriptScavenger = safeStart(new TranscriptScavengerAdapter(emit, workspace, user, host), 'org.omnitype.adapters.transcript-scavenger', health);
|
|
36
|
+
return {
|
|
37
|
+
filesystemOnly,
|
|
38
|
+
hookPoller,
|
|
39
|
+
transcriptScavenger,
|
|
40
|
+
getHealth: () => health.slice(),
|
|
41
|
+
stop() {
|
|
42
|
+
try {
|
|
43
|
+
filesystemOnly.stopAll();
|
|
44
|
+
}
|
|
45
|
+
catch { /* */ }
|
|
46
|
+
try {
|
|
47
|
+
hookPoller?.stop();
|
|
48
|
+
}
|
|
49
|
+
catch { /* */ }
|
|
50
|
+
try {
|
|
51
|
+
transcriptScavenger?.stop();
|
|
52
|
+
}
|
|
53
|
+
catch { /* */ }
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export { FilesystemOnlyAdapter } from './filesystem-only.js';
|
|
58
|
+
export { HookPoller } from './hook-poller.js';
|
|
59
|
+
export { TranscriptScavengerAdapter } from './transcript-scavenger.js';
|
|
60
|
+
export { installHooks, TOOL_SPECS, buildHookOneliner } from './stdin-json-hook-tool.js';
|
|
61
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/adapters/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAiBvE,SAAS,SAAS,CAChB,OAAU,EACV,EAAU,EACV,MAAuB;IAEvB,IAAI,CAAC;QACH,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACnC,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC7D,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,qBAAqB,GAAG,EAAE,CAAC,CAAC;QACnE,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,IAAY,EACZ,SAAiB,EACjB,IAAY,EACZ,IAAY;IAEZ,MAAM,MAAM,GAAoB,EAAE,CAAC;IAEnC,wEAAwE;IACxE,+EAA+E;IAC/E,MAAM,cAAc,GAAG,IAAI,qBAAqB,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9E,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,uCAAuC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAE5E,MAAM,UAAU,GAAG,SAAS,CAC1B,IAAI,UAAU,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,EAC3C,mCAAmC,EACnC,MAAM,CACP,CAAC;IAEF,MAAM,mBAAmB,GAAG,SAAS,CACnC,IAAI,0BAA0B,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,EAC3D,4CAA4C,EAC5C,MAAM,CACP,CAAC;IAEF,OAAO;QACL,cAAc;QACd,UAAU;QACV,mBAAmB;QACnB,SAAS,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE;QAC/B,IAAI;YACF,IAAI,CAAC;gBAAC,cAAc,CAAC,OAAO,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;YACjD,IAAI,CAAC;gBAAC,UAAU,EAAE,IAAI,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;YAC3C,IAAI,CAAC;gBAAC,mBAAmB,EAAE,IAAI,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC;QACtD,CAAC;KACF,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC"}
|