@jmtrin/opencode-kevin 1.1.0 → 1.3.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 (131) hide show
  1. package/dist/plugin/capabilities.d.ts +2 -0
  2. package/dist/plugin/capabilities.js +3 -0
  3. package/dist/plugin/index.d.ts +5 -4
  4. package/dist/plugin/index.js +401 -108
  5. package/dist/plugin/native.d.ts +2 -2
  6. package/dist/plugin/native.js +2 -2
  7. package/package.json +56 -63
  8. package/LICENSE +0 -21
  9. package/README.md +0 -576
  10. package/dist/migrations/001_initial.sql +0 -92
  11. package/dist/migrations/002_indexes.sql +0 -14
  12. package/dist/migrations/003_v02_signal.sql +0 -58
  13. package/dist/migrations/004_v03_knowledge.sql +0 -138
  14. package/dist/migrations/005_v04_signal.sql +0 -57
  15. package/dist/migrations/006_v05_glassbox.sql +0 -118
  16. package/dist/migrations/007_v06_pull.sql +0 -145
  17. package/dist/migrations/008_v07_truth.sql +0 -124
  18. package/dist/migrations/009_v08_team.sql +0 -100
  19. package/dist/migrations/010_v09_native.sql +0 -78
  20. package/dist/migrations/011_v10_proven.sql +0 -69
  21. package/dist/migrations/012_v11_drift.sql +0 -24
  22. package/dist/plugin/Archiver.d.ts +0 -42
  23. package/dist/plugin/Archiver.js +0 -83
  24. package/dist/plugin/ArtifactWriter.d.ts +0 -93
  25. package/dist/plugin/ArtifactWriter.js +0 -291
  26. package/dist/plugin/CausalChain.d.ts +0 -22
  27. package/dist/plugin/CausalChain.js +0 -197
  28. package/dist/plugin/ConflictDetector.d.ts +0 -35
  29. package/dist/plugin/ConflictDetector.js +0 -260
  30. package/dist/plugin/ContextInjector.d.ts +0 -222
  31. package/dist/plugin/ContextInjector.js +0 -533
  32. package/dist/plugin/ConventionMiner.d.ts +0 -35
  33. package/dist/plugin/ConventionMiner.js +0 -242
  34. package/dist/plugin/Curator.d.ts +0 -116
  35. package/dist/plugin/Curator.js +0 -324
  36. package/dist/plugin/Feedback.d.ts +0 -67
  37. package/dist/plugin/Feedback.js +0 -120
  38. package/dist/plugin/HookLiveness.d.ts +0 -88
  39. package/dist/plugin/HookLiveness.js +0 -322
  40. package/dist/plugin/InjectionLedger.d.ts +0 -98
  41. package/dist/plugin/InjectionLedger.js +0 -295
  42. package/dist/plugin/LessonFixer.d.ts +0 -44
  43. package/dist/plugin/LessonFixer.js +0 -45
  44. package/dist/plugin/Materializer.d.ts +0 -83
  45. package/dist/plugin/Materializer.js +0 -181
  46. package/dist/plugin/MemoryService.d.ts +0 -360
  47. package/dist/plugin/MemoryService.js +0 -1242
  48. package/dist/plugin/Migrate.d.ts +0 -17
  49. package/dist/plugin/Migrate.js +0 -271
  50. package/dist/plugin/PatternMiner.d.ts +0 -49
  51. package/dist/plugin/PatternMiner.js +0 -132
  52. package/dist/plugin/QualityGate.d.ts +0 -124
  53. package/dist/plugin/QualityGate.js +0 -119
  54. package/dist/plugin/Reflector.d.ts +0 -91
  55. package/dist/plugin/Reflector.js +0 -307
  56. package/dist/plugin/RepoIdentity.d.ts +0 -124
  57. package/dist/plugin/RepoIdentity.js +0 -300
  58. package/dist/plugin/RepoTruth.d.ts +0 -80
  59. package/dist/plugin/RepoTruth.js +0 -599
  60. package/dist/plugin/Retrospective.d.ts +0 -17
  61. package/dist/plugin/Retrospective.js +0 -213
  62. package/dist/plugin/SharedLayer.d.ts +0 -159
  63. package/dist/plugin/SharedLayer.js +0 -462
  64. package/dist/plugin/Store.d.ts +0 -29
  65. package/dist/plugin/Store.js +0 -50
  66. package/dist/plugin/ToolCallObserver.d.ts +0 -32
  67. package/dist/plugin/ToolCallObserver.js +0 -201
  68. package/dist/plugin/columns.d.ts +0 -11
  69. package/dist/plugin/columns.js +0 -54
  70. package/dist/plugin/confidence.d.ts +0 -8
  71. package/dist/plugin/confidence.js +0 -34
  72. package/dist/plugin/contract.d.ts +0 -54
  73. package/dist/plugin/contract.js +0 -383
  74. package/dist/plugin/diff.d.ts +0 -8
  75. package/dist/plugin/diff.js +0 -182
  76. package/dist/plugin/escape.d.ts +0 -39
  77. package/dist/plugin/escape.js +0 -60
  78. package/dist/plugin/fingerprint.d.ts +0 -27
  79. package/dist/plugin/fingerprint.js +0 -73
  80. package/dist/plugin/inferability.d.ts +0 -32
  81. package/dist/plugin/inferability.js +0 -88
  82. package/dist/plugin/kevin_approve.d.ts +0 -34
  83. package/dist/plugin/kevin_approve.js +0 -52
  84. package/dist/plugin/kevin_audit.d.ts +0 -213
  85. package/dist/plugin/kevin_audit.js +0 -563
  86. package/dist/plugin/kevin_bench.d.ts +0 -10
  87. package/dist/plugin/kevin_bench.js +0 -82
  88. package/dist/plugin/kevin_conflicts.d.ts +0 -9
  89. package/dist/plugin/kevin_conflicts.js +0 -50
  90. package/dist/plugin/kevin_contract.d.ts +0 -8
  91. package/dist/plugin/kevin_contract.js +0 -59
  92. package/dist/plugin/kevin_doctor.d.ts +0 -62
  93. package/dist/plugin/kevin_doctor.js +0 -196
  94. package/dist/plugin/kevin_facts.d.ts +0 -42
  95. package/dist/plugin/kevin_facts.js +0 -36
  96. package/dist/plugin/kevin_forget.d.ts +0 -33
  97. package/dist/plugin/kevin_forget.js +0 -260
  98. package/dist/plugin/kevin_native.d.ts +0 -29
  99. package/dist/plugin/kevin_native.js +0 -79
  100. package/dist/plugin/kevin_propose.d.ts +0 -23
  101. package/dist/plugin/kevin_propose.js +0 -14
  102. package/dist/plugin/kevin_publish.d.ts +0 -38
  103. package/dist/plugin/kevin_publish.js +0 -18
  104. package/dist/plugin/kevin_why.d.ts +0 -23
  105. package/dist/plugin/kevin_why.js +0 -111
  106. package/dist/plugin/memory-format.d.ts +0 -20
  107. package/dist/plugin/memory-format.js +0 -59
  108. package/dist/plugin/metrics.d.ts +0 -97
  109. package/dist/plugin/metrics.js +0 -284
  110. package/dist/plugin/okf-export.d.ts +0 -3
  111. package/dist/plugin/okf-export.js +0 -134
  112. package/dist/plugin/okf-import.d.ts +0 -76
  113. package/dist/plugin/okf-import.js +0 -271
  114. package/dist/plugin/okf.d.ts +0 -107
  115. package/dist/plugin/okf.js +0 -303
  116. package/dist/plugin/perf.d.ts +0 -35
  117. package/dist/plugin/perf.js +0 -174
  118. package/dist/plugin/query-tokenizer.d.ts +0 -13
  119. package/dist/plugin/query-tokenizer.js +0 -133
  120. package/dist/plugin/redact.d.ts +0 -2
  121. package/dist/plugin/redact.js +0 -18
  122. package/dist/plugin/replay-types.d.ts +0 -69
  123. package/dist/plugin/replay-types.js +0 -156
  124. package/dist/plugin/replay.d.ts +0 -36
  125. package/dist/plugin/replay.js +0 -202
  126. package/dist/plugin/sqlite-adapter.d.ts +0 -12
  127. package/dist/plugin/sqlite-adapter.js +0 -81
  128. package/dist/plugin/time-ms.d.ts +0 -1
  129. package/dist/plugin/time-ms.js +0 -16
  130. package/dist/plugin/uuid.d.ts +0 -1
  131. package/dist/plugin/uuid.js +0 -50
@@ -1,201 +0,0 @@
1
- import { hasColumn } from "./columns.js";
2
- import { fingerprint as computeFingerprint } from "./fingerprint.js";
3
- import { redactPaths, stripPrivate } from "./redact.js";
4
- import { uuidv7 } from "./uuid.js";
5
- const SECRET_PATTERNS = [
6
- /\b(API_KEY|SECRET|PASSWORD|TOKEN)\b\s*[=:]\s*\S+/gi,
7
- /\bBearer\s+\S+/gi,
8
- // BUG-013 — the old `\btoken\s+\S+/gi` mangled harmless phrasing
9
- // ("token budget", "token count", "max token limit") that LLM tools
10
- // emit routinely. Narrowed to credential contexts: named token
11
- // variables WITH an assignment, and `token=<value>` / `token:
12
- // <value>` assignments.
13
- /\b(access_?token|auth_?token|api_?token)\b\s*[=:]\s*\S+/gi,
14
- /\btoken\s*[=:]\s*\S+/gi,
15
- ];
16
- export class ToolCallObserver {
17
- store;
18
- startTs = new Map();
19
- metrics;
20
- constructor(store, metrics) {
21
- this.store = store;
22
- this.metrics = metrics ?? null;
23
- }
24
- onBefore(input, _output) {
25
- const key = this.key(input);
26
- this.startTs.set(key, Date.now());
27
- }
28
- onAfter(input, output) {
29
- const key = this.key(input);
30
- const start = this.startTs.get(key) ?? Date.now();
31
- this.startTs.delete(key);
32
- const durationMs = Math.max(0, Date.now() - start);
33
- const sessionId = input.sessionId ?? "unknown";
34
- const argsSummary = this.summarizeArgs(input.args ?? {});
35
- const success = output.success === true ? 1 : 0;
36
- const agent = input.agent ?? null;
37
- const stderr = stripPrivate(output.stderr ?? "");
38
- const stdout = stripPrivate(output.stdout ?? "");
39
- const errorType = output.success === false
40
- ? this.inferErrorType(stderr, stdout, output.exitCode)
41
- : null;
42
- const metadata = JSON.stringify(this.redactArgs(input.args ?? {}));
43
- const projectId = input.projectId ?? null;
44
- const fp = computeFingerprint(`${input.tool}|${argsSummary}|${success}`, projectId ?? undefined);
45
- if (this.isDedupEnabled()) {
46
- const existing = this.store
47
- .prepare(`SELECT 1 FROM tool_calls
48
- WHERE fingerprint = ?
49
- AND (project_id IS ? OR (project_id IS NULL AND ? IS NULL))
50
- AND strftime('%Y-%m-%d %H:%M', ts) = strftime('%Y-%m-%d %H:%M', 'now')
51
- LIMIT 1`)
52
- .get(fp, projectId, projectId);
53
- if (existing) {
54
- this.metrics?.incr("tool_calls_deduped", 1);
55
- return;
56
- }
57
- }
58
- // v1.1.0 (K11-002 / plan §5.2, D11-01/D11-07) — dual-write: legacy `ts`
59
- // stays (`datetime('now')`), new `ts_ms` holds Date.now() when the
60
- // column exists. Probe is cached via columns registry (K11-011).
61
- if (hasColumn(this.store, "tool_calls", "ts_ms")) {
62
- this.store
63
- .prepare(`INSERT INTO tool_calls
64
- (id, session_id, ts, ts_ms, tool, args_summary, success, duration_ms, agent, error_type, metadata, project_id, fingerprint)
65
- VALUES (?, ?, datetime('now'), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
66
- .run(input.callID ?? uuidv7(), sessionId, Date.now(), input.tool, argsSummary, success, durationMs, agent, errorType, metadata, projectId, fp);
67
- }
68
- else {
69
- this.store
70
- .prepare(`INSERT INTO tool_calls
71
- (id, session_id, ts, tool, args_summary, success, duration_ms, agent, error_type, metadata, project_id, fingerprint)
72
- VALUES (?, ?, datetime('now'), ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
73
- .run(input.callID ?? uuidv7(), sessionId, input.tool, argsSummary, success, durationMs, agent, errorType, metadata, projectId, fp);
74
- }
75
- }
76
- isDedupEnabled() {
77
- try {
78
- const row = this.store
79
- .prepare("SELECT value FROM kevin_settings WHERE key = 'tool_calls_dedup_enabled'")
80
- .get();
81
- return row?.value === "1";
82
- }
83
- catch {
84
- return false;
85
- }
86
- }
87
- redactSecrets(text) {
88
- let out = text;
89
- for (const pat of SECRET_PATTERNS) {
90
- out = out.replace(pat, (match) => {
91
- // BUG-013 — preserve the original separator so
92
- // `auth_token = abc123` keeps its spacing: only the
93
- // value is replaced.
94
- const m = match.match(/^(.+?)(\s*[=:]\s*)(\S+)$/);
95
- if (m) {
96
- return `${m[1]}${m[2]}<redacted>`;
97
- }
98
- const parts = match.split(/\s+/);
99
- return `${parts[0]} <redacted>`;
100
- });
101
- }
102
- return out;
103
- }
104
- summarizeArgs(args) {
105
- const interestingKeys = [
106
- "filePath",
107
- "path",
108
- "cwd",
109
- "command",
110
- "cmd",
111
- "file",
112
- "directory",
113
- ];
114
- const parts = [];
115
- for (const [k, v] of Object.entries(args)) {
116
- if (interestingKeys.includes(k)) {
117
- parts.push(`${k}: ${this.redactValue(v)}`);
118
- }
119
- else if (this.looksSecret(k)) {
120
- parts.push(`${k}: <redacted>`);
121
- }
122
- else {
123
- parts.push(`${k}: ${this.redactValue(v, 200)}`);
124
- }
125
- }
126
- let summary = parts.join(", ");
127
- if (summary.length > 500)
128
- summary = `${summary.slice(0, 500)}...`;
129
- return summary;
130
- }
131
- inferErrorType(stderr, stdout, exitCode) {
132
- const combined = `${stderr}\n${stdout}`.toLowerCase();
133
- if (exitCode === 124 ||
134
- /timed out|timeout|etimedout|\bkilled\b|sigterm|sigkill/.test(combined)) {
135
- return "timeout";
136
- }
137
- if (/error ts|tsc|\btypescript\b/.test(combined))
138
- return "typecheck";
139
- if (/\b(lint|biome|eslint)\b/.test(combined))
140
- return "lint";
141
- if (/\b(fail|vitest|jest|test failed)\b/.test(combined))
142
- return "test";
143
- if (/error:|typeerror|referenceerror|syntaxerror/.test(combined))
144
- return "runtime";
145
- if ((exitCode === undefined || exitCode === -1) &&
146
- stderr.trim() === "" &&
147
- stdout.trim() === "") {
148
- return "timeout";
149
- }
150
- return "unknown";
151
- }
152
- key(input) {
153
- if (input.callID)
154
- return `callID::${input.callID}`;
155
- const argsKey = JSON.stringify(input.args ?? {});
156
- return `${input.sessionId ?? "unknown"}::${input.tool}::${argsKey}`;
157
- }
158
- looksSecret(key) {
159
- const k = key.toLowerCase();
160
- return /(key|secret|password|token|bearer)/.test(k);
161
- }
162
- redactArgs(args) {
163
- const out = {};
164
- for (const [k, v] of Object.entries(args)) {
165
- if (this.looksSecret(k)) {
166
- out[k] = "<redacted>";
167
- }
168
- else {
169
- out[k] = this.redactValue(v);
170
- }
171
- }
172
- return out;
173
- }
174
- redactValue(v, truncateAt) {
175
- if (typeof v === "string") {
176
- let s = stripPrivate(v);
177
- s = redactPaths(s);
178
- s = this.redactSecrets(s);
179
- if (truncateAt !== undefined && s.length > truncateAt) {
180
- s = `${s.slice(0, truncateAt)}...`;
181
- }
182
- return s;
183
- }
184
- if (Array.isArray(v)) {
185
- return v.map((item) => this.redactValue(item));
186
- }
187
- if (v && typeof v === "object") {
188
- const obj = {};
189
- for (const [k, val] of Object.entries(v)) {
190
- if (this.looksSecret(k)) {
191
- obj[k] = "<redacted>";
192
- }
193
- else {
194
- obj[k] = this.redactValue(val);
195
- }
196
- }
197
- return obj;
198
- }
199
- return v;
200
- }
201
- }
@@ -1,11 +0,0 @@
1
- import type { Store } from "./Store.js";
2
- export declare function hasColumn(store: Store, table: string, column: string): boolean;
3
- export declare function hasIgnoredColumn(store: Store): boolean;
4
- export declare function hasCuratedColumn(store: Store): boolean;
5
- export declare function hasTruthColumns(store: Store): boolean;
6
- export declare function hasRepoIdColumn(store: Store): boolean;
7
- export declare function hasLayerColumn(store: Store): boolean;
8
- export declare function hasRecurrenceColumn(store: Store): boolean;
9
- export declare function hasArchivedColumn(store: Store): boolean;
10
- export declare function hasFeedbackColumns(store: Store): boolean;
11
- export declare function hasFeedbackTable(store: Store): boolean;
@@ -1,54 +0,0 @@
1
- const cache = new WeakMap();
2
- export function hasColumn(store, table, column) {
3
- const byStore = cache.get(store);
4
- const key = `${table}.${column}`;
5
- if (byStore?.get(key) === true)
6
- return true;
7
- try {
8
- store.prepare(`SELECT ${column} FROM ${table} LIMIT 0`).get();
9
- let m = byStore;
10
- if (!m) {
11
- m = new Map();
12
- cache.set(store, m);
13
- }
14
- m.set(key, true);
15
- return true;
16
- }
17
- catch {
18
- return false;
19
- }
20
- }
21
- // v1.1.0 (K11-011 / plan §5.5, D11-06) — named helpers delegating to the registry
22
- export function hasIgnoredColumn(store) {
23
- return hasColumn(store, "memories", "ignored");
24
- }
25
- export function hasCuratedColumn(store) {
26
- return hasColumn(store, "memories", "curated");
27
- }
28
- export function hasTruthColumns(store) {
29
- return hasColumn(store, "memories", "truth_penalty");
30
- }
31
- export function hasRepoIdColumn(store) {
32
- return hasColumn(store, "memories", "repo_id");
33
- }
34
- export function hasLayerColumn(store) {
35
- return hasColumn(store, "memories", "layer");
36
- }
37
- export function hasRecurrenceColumn(store) {
38
- return hasColumn(store, "memories", "recurrence_count");
39
- }
40
- export function hasArchivedColumn(store) {
41
- return hasColumn(store, "memories", "archived_at");
42
- }
43
- export function hasFeedbackColumns(store) {
44
- return hasColumn(store, "memories", "feedback_positive");
45
- }
46
- export function hasFeedbackTable(store) {
47
- try {
48
- store.prepare("SELECT COUNT(*) FROM memory_feedback LIMIT 0").get();
49
- return true;
50
- }
51
- catch {
52
- return false;
53
- }
54
- }
@@ -1,8 +0,0 @@
1
- export declare const CONFIDENCE_MIN = 0.05;
2
- export declare const CONFIDENCE_MAX = 0.95;
3
- export declare const CONFIDENCE_BASE = 0.5;
4
- export declare const EVIDENCE_STEP = 0.1;
5
- export declare const RECURRENCE_PENALTY = 0.15;
6
- export declare const FEEDBACK_POSITIVE_STEP = 0.05;
7
- export declare const FEEDBACK_NEGATIVE_STEP = 0.1;
8
- export declare function computeConfidence(evidenceCount: number, recurrenceCount: number, positiveFeedback?: number, negativeFeedback?: number): number;
@@ -1,34 +0,0 @@
1
- // v0.4.0 (K4-010) — shared two-sided confidence formula.
2
- //
3
- // Confidence is derived from both positive evidence (confirmed fixes) and
4
- // negative evidence (recurrences): recurrence of the same fingerprint
5
- // DEMOTES a pattern's confidence, so that a "causal pattern" with a
6
- // history of recurring failures stops being presented as reliable.
7
- //
8
- // confidence = clamp(0.5 + 0.1 * evidenceCount - 0.15 * recurrenceCount, 0.05, 0.95)
9
- //
10
- // Used by `MemoryService.promoteToPattern`, `MemoryService.mapRow` and
11
- // `kevin_why` — keep the formula here, never inline it again.
12
- //
13
- // v0.5.0 (K5-010 / plan §5.3, D5-02) — human feedback joins the formula as
14
- // a second, independent evidence axis. Steps are deliberately SMALLER than
15
- // the causal steps: feedback is sparse, one-sided, and judgemental, while
16
- // evidence_count counts confirmed, reproducible fixes. A single negative
17
- // verdict (0.1) can offset one confirmed fix (0.1), but cannot alone sink
18
- // a well-evidenced pattern — the causal evidence must agree (plan §5.3,
19
- // "feedback cannot override causal evidence, only nudge it").
20
- export const CONFIDENCE_MIN = 0.05;
21
- export const CONFIDENCE_MAX = 0.95;
22
- export const CONFIDENCE_BASE = 0.5;
23
- export const EVIDENCE_STEP = 0.1;
24
- export const RECURRENCE_PENALTY = 0.15;
25
- export const FEEDBACK_POSITIVE_STEP = 0.05;
26
- export const FEEDBACK_NEGATIVE_STEP = 0.1;
27
- export function computeConfidence(evidenceCount, recurrenceCount, positiveFeedback = 0, negativeFeedback = 0) {
28
- const raw = CONFIDENCE_BASE +
29
- EVIDENCE_STEP * evidenceCount -
30
- RECURRENCE_PENALTY * recurrenceCount +
31
- FEEDBACK_POSITIVE_STEP * positiveFeedback -
32
- FEEDBACK_NEGATIVE_STEP * negativeFeedback;
33
- return Math.min(CONFIDENCE_MAX, Math.max(CONFIDENCE_MIN, raw));
34
- }
@@ -1,54 +0,0 @@
1
- export declare const CONTRACT_VERSION = 1;
2
- export type Stability = "frozen" | "forward-only";
3
- export interface ContractClause {
4
- readonly id: string;
5
- readonly title: string;
6
- readonly stability: Stability;
7
- readonly since: string;
8
- readonly deprecated?: string;
9
- readonly replacement?: string;
10
- readonly value: unknown;
11
- }
12
- export interface PublicContract {
13
- readonly contractVersion: number;
14
- readonly clauses: readonly ContractClause[];
15
- }
16
- export interface ContractInput {
17
- readonly packageName?: string;
18
- readonly packageVersion?: string;
19
- }
20
- export declare const CONTRACT_TOOL_NAMES: readonly string[];
21
- /**
22
- * v1.0.0 (K10-018/K10-019 / plan §5.6) — tools added AFTER the initial
23
- * freeze, each carrying the `since` the deprecation policy requires. A
24
- * 1.x addition without an entry here fails the contract test as
25
- * added_bare.
26
- */
27
- export declare const CONTRACT_TOOL_ADDITIONS: readonly {
28
- name: string;
29
- since: string;
30
- }[];
31
- /**
32
- * v1.1.0 (K11-007 / plan §5.1, D11-01) — metric keys added after the freeze,
33
- * each carrying the `since` the deprecation policy requires (C-05).
34
- */
35
- export declare const CONTRACT_METRIC_ADDITIONS: readonly {
36
- name: string;
37
- since: string;
38
- }[];
39
- /**
40
- * v1.0.0 (K10-027 / plan §5.7) — the C-09 boundary addition. Stored is
41
- * not trusted: anything reaching an artifact or a prompt is escaped at
42
- * the single write path, according to its container.
43
- */
44
- export declare const BOUNDARY_INVARIANT = "untrusted-input escaping at the single write path";
45
- export declare function describeContract(_input?: ContractInput): PublicContract;
46
- export declare function contractDigest(c: PublicContract): string;
47
- export type ContractDiffKind = "removed" | "changed" | "added_ok" | "added_bare";
48
- export interface ContractDiff {
49
- readonly clauseId: string;
50
- readonly path: string;
51
- readonly kind: ContractDiffKind;
52
- readonly remedy: string;
53
- }
54
- export declare function diffContract(golden: PublicContract, live: PublicContract): readonly ContractDiff[];