@feltdb/core 0.6.8 → 0.6.10

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 (99) hide show
  1. package/dist/cli/application.js +55 -88
  2. package/dist/cli/commands.js +206 -95
  3. package/dist/cli/index.js +1 -1
  4. package/dist/collection.d.ts +110 -2
  5. package/dist/collection.d.ts.map +1 -1
  6. package/dist/collection.js +228 -3
  7. package/dist/create/package-versions.js +1 -1
  8. package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +408 -178
  9. package/dist/create/server-source/crates/feltdb/src/lib.rs +49 -5
  10. package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +659 -0
  11. package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +285 -0
  12. package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +582 -0
  13. package/dist/create/server-source/crates/feltdb-server/src/main.rs +141 -0
  14. package/dist/db.d.ts +59 -0
  15. package/dist/db.d.ts.map +1 -1
  16. package/dist/db.js +94 -2
  17. package/dist/embedded-transaction.d.ts +77 -0
  18. package/dist/embedded-transaction.d.ts.map +1 -0
  19. package/dist/embedded-transaction.js +102 -0
  20. package/dist/feltdb.d.ts +49 -1
  21. package/dist/feltdb.d.ts.map +1 -1
  22. package/dist/file-db.d.ts +33 -0
  23. package/dist/file-db.d.ts.map +1 -1
  24. package/dist/file-db.js +128 -4
  25. package/dist/freshness.d.ts +119 -0
  26. package/dist/freshness.d.ts.map +1 -0
  27. package/dist/freshness.js +115 -0
  28. package/dist/http-db.d.ts +48 -0
  29. package/dist/http-db.d.ts.map +1 -1
  30. package/dist/http-db.js +77 -1
  31. package/dist/index-backend.d.ts +8 -0
  32. package/dist/index-backend.d.ts.map +1 -1
  33. package/dist/index-backend.js +13 -0
  34. package/dist/index-core.d.ts +78 -0
  35. package/dist/index-core.d.ts.map +1 -0
  36. package/dist/index-core.js +72 -0
  37. package/dist/index-manager.d.ts +11 -0
  38. package/dist/index-manager.d.ts.map +1 -1
  39. package/dist/index-manager.js +30 -13
  40. package/dist/index-types.d.ts +17 -0
  41. package/dist/index-types.d.ts.map +1 -1
  42. package/dist/index.browser.d.ts +14 -0
  43. package/dist/index.browser.d.ts.map +1 -0
  44. package/dist/index.browser.js +13 -0
  45. package/dist/index.d.ts +8 -59
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +11 -54
  48. package/dist/indexeddb-db.d.ts +32 -0
  49. package/dist/indexeddb-db.d.ts.map +1 -1
  50. package/dist/indexeddb-db.js +151 -1
  51. package/dist/memory-db.d.ts +26 -0
  52. package/dist/memory-db.d.ts.map +1 -1
  53. package/dist/memory-db.js +86 -0
  54. package/dist/runtime-registry.d.ts +32 -0
  55. package/dist/runtime-registry.d.ts.map +1 -0
  56. package/dist/runtime-registry.js +38 -0
  57. package/dist/studio-app/assets/{feltdb_wasm-B4wq4mqp.js → feltdb_wasm-CJv3wHzi.js} +1 -1
  58. package/dist/studio-app/assets/feltdb_wasm_bg-C8TG8r2n.wasm +0 -0
  59. package/dist/studio-app/assets/index-D3rT0SJi.js +28 -0
  60. package/dist/studio-app/index.html +1 -1
  61. package/dist/transaction.d.ts +98 -0
  62. package/dist/transaction.d.ts.map +1 -0
  63. package/dist/transaction.js +86 -0
  64. package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
  65. package/dist/workspace/browser.d.ts +22 -0
  66. package/dist/workspace/browser.d.ts.map +1 -0
  67. package/dist/workspace/browser.js +17 -0
  68. package/dist/workspace/git-identity.d.ts +35 -0
  69. package/dist/workspace/git-identity.d.ts.map +1 -0
  70. package/dist/workspace/git-identity.js +81 -0
  71. package/dist/workspace/index.d.ts +11 -1
  72. package/dist/workspace/index.d.ts.map +1 -1
  73. package/dist/workspace/index.js +6 -0
  74. package/dist/workspace/investigation-analysis.d.ts +47 -0
  75. package/dist/workspace/investigation-analysis.d.ts.map +1 -0
  76. package/dist/workspace/investigation-analysis.js +181 -0
  77. package/dist/workspace/investigation-lifecycle-manager.d.ts +86 -0
  78. package/dist/workspace/investigation-lifecycle-manager.d.ts.map +1 -0
  79. package/dist/workspace/investigation-lifecycle-manager.js +229 -0
  80. package/dist/workspace/investigation-supervisor.d.ts +100 -0
  81. package/dist/workspace/investigation-supervisor.d.ts.map +1 -0
  82. package/dist/workspace/investigation-supervisor.js +236 -0
  83. package/dist/workspace/runtime-investigation.d.ts +1 -1
  84. package/dist/workspace/runtime-investigation.d.ts.map +1 -1
  85. package/dist/workspace/runtime-investigation.js +46 -21
  86. package/dist/workspace/runtime-observation.d.ts +79 -0
  87. package/dist/workspace/runtime-observation.d.ts.map +1 -0
  88. package/dist/workspace/runtime-observation.js +152 -0
  89. package/dist/workspace/runtime-observer.d.ts +51 -0
  90. package/dist/workspace/runtime-observer.d.ts.map +1 -0
  91. package/dist/workspace/runtime-observer.js +268 -0
  92. package/dist/workspace/workspace-connection.d.ts.map +1 -1
  93. package/dist/workspace/workspace-connection.js +14 -5
  94. package/dist/workspace/workspace-types.d.ts +113 -0
  95. package/dist/workspace/workspace-types.d.ts.map +1 -1
  96. package/package.json +12 -4
  97. package/dist/create/server-source/crates/feltdb/src/phase_1c_three_node.rs +0 -523
  98. package/dist/studio-app/assets/feltdb_wasm_bg-Ceyi7l21.wasm +0 -0
  99. package/dist/studio-app/assets/index-BF45M757.js +0 -28
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Runtime investigation supervisor.
3
+ *
4
+ * Reads durable runtime observations out of the Development Workspace, opens
5
+ * investigations for the ones that represent actionable defects, builds the
6
+ * evidence and the finding, produces the agent-executable remediation
7
+ * contract, tracks the repository change, and verifies the outcome against a
8
+ * later runtime observation.
9
+ *
10
+ * The architectural boundary is enforced here: this supervisor observes,
11
+ * investigates, contracts, verifies, and remembers. It never edits source and
12
+ * it never decides that a defect is fixed on any basis other than a runtime
13
+ * observation.
14
+ */
15
+ import fs from 'node:fs';
16
+ import path from 'node:path';
17
+ import { RUNTIME_OBSERVATION_COLLECTION, isActionableRuntimeDefect } from './runtime-observation.js';
18
+ import { collectEvidence, candidateSourcePaths, deriveFinding, recommendChanges, resolveRelevantSource, summarizeDefect, verificationContractFor, } from './investigation-analysis.js';
19
+ import { InvestigationLifecycleManager } from './investigation-lifecycle-manager.js';
20
+ import { captureRepositoryIdentity } from './git-identity.js';
21
+ import { agentRemediationHandoff } from './runtime-investigation.js';
22
+ import { connectDevelopmentWorkspace } from './workspace-connection.js';
23
+ export class RuntimeInvestigationSupervisor {
24
+ constructor(options) {
25
+ this.connection = null;
26
+ this.seenObservations = new Set();
27
+ this.options = options;
28
+ this.investigations = new InvestigationLifecycleManager(options.projectDir);
29
+ // Observations already turned into investigations must not re-open on a
30
+ // restart, so the seen set is rebuilt from durable state.
31
+ for (const investigation of this.investigations.listInvestigations()) {
32
+ this.seenObservations.add(investigation.observationId);
33
+ }
34
+ }
35
+ async connect() {
36
+ if (this.connection)
37
+ return;
38
+ this.connection = await connectDevelopmentWorkspace({
39
+ workspaceId: this.options.workspaceId,
40
+ endpoint: this.options.endpoint,
41
+ clientType: 'agent',
42
+ });
43
+ }
44
+ async disconnect() {
45
+ await this.connection?.disconnect();
46
+ this.connection = null;
47
+ }
48
+ require() {
49
+ if (!this.connection)
50
+ throw new Error('Supervisor is not connected to the workspace');
51
+ return this.connection;
52
+ }
53
+ /** Every runtime observation the workspace has persisted. */
54
+ async observations() {
55
+ const records = await this.require().query(RUNTIME_OBSERVATION_COLLECTION);
56
+ return records.sort((left, right) => left.timestamp - right.timestamp);
57
+ }
58
+ /**
59
+ * Wait for a runtime observation matching a verification criterion.
60
+ *
61
+ * Returns undefined on timeout, which is what produces an INCONCLUSIVE
62
+ * verification rather than a false VERIFIED.
63
+ */
64
+ async awaitObservation(criterion, options = { after: 0 }) {
65
+ const deadline = Date.now() + (options.timeoutMs ?? 10000);
66
+ const interval = options.intervalMs ?? 100;
67
+ while (Date.now() < deadline) {
68
+ const match = (await this.observations()).find(observation => observation.timestamp > options.after
69
+ && observation.method.toUpperCase() === criterion.method.toUpperCase()
70
+ && samePath(observation.url, criterion.url));
71
+ if (match)
72
+ return match;
73
+ await delay(interval);
74
+ }
75
+ return undefined;
76
+ }
77
+ /**
78
+ * Open investigations for actionable defects that do not have one yet.
79
+ *
80
+ * Successful requests are persisted as observations but never open an
81
+ * investigation.
82
+ */
83
+ async ingestObservations() {
84
+ const opened = [];
85
+ for (const observation of await this.observations()) {
86
+ if (this.seenObservations.has(observation.observationId))
87
+ continue;
88
+ this.seenObservations.add(observation.observationId);
89
+ if (!isActionableRuntimeDefect(observation))
90
+ continue;
91
+ opened.push(this.openInvestigation(observation));
92
+ }
93
+ return opened;
94
+ }
95
+ /**
96
+ * Drive a single observation through investigation and analysis.
97
+ *
98
+ * Ends in PROPOSED with evidence, a finding, and a remediation contract, and
99
+ * with the repository baseline recorded so the change can be attributed.
100
+ */
101
+ openInvestigation(observation) {
102
+ const created = this.investigations.createInvestigation(observation, this.options.workspaceId);
103
+ const evidence = collectEvidence(created.id, observation);
104
+ this.investigations.transitionToInvestigating(created.id, evidence);
105
+ const finding = deriveFinding(created.id, observation, evidence);
106
+ this.investigations.transitionToFinding(created.id, finding);
107
+ const contract = {
108
+ investigationId: created.id,
109
+ originalObservation: observation,
110
+ finding: finding.statement,
111
+ evidence: evidence.map(item => item.statement),
112
+ relevantSource: this.relevantSource(observation.url),
113
+ recommendedChanges: recommendChanges(observation),
114
+ verificationCriteria: verificationContractFor(observation),
115
+ };
116
+ return this.investigations.sendToAgent(created.id, contract, this.repositoryIdentity());
117
+ }
118
+ /** Repository state, used as the remediation baseline. */
119
+ repositoryIdentity() {
120
+ return captureRepositoryIdentity(this.options.repositoryDir || this.options.projectDir);
121
+ }
122
+ /** Source files that exist on disk and could implement the observed route. */
123
+ relevantSource(url) {
124
+ const root = this.options.repositoryDir || this.options.projectDir;
125
+ const existing = candidateSourcePaths(url).filter(candidate => fs.existsSync(path.join(root, candidate)));
126
+ return resolveRelevantSource(url, existing);
127
+ }
128
+ /**
129
+ * Write the agent handoff artifact.
130
+ *
131
+ * FeltDB stops here: it produces a complete, machine-readable engineering
132
+ * contract and hands it over. Inspecting the repository and editing source
133
+ * is the coding agent's job, not FeltDB's.
134
+ */
135
+ writeAgentHandoff(investigationId) {
136
+ const investigation = this.investigations.loadInvestigation(investigationId);
137
+ if (!investigation?.remediationContract) {
138
+ throw new Error(`Investigation ${investigationId} has no remediation contract`);
139
+ }
140
+ const contract = investigation.remediationContract;
141
+ // Handoff artifacts live beside the investigation store, never inside it:
142
+ // a `<id>.agent.json` in the store would be read back as an investigation.
143
+ const directory = path.join(this.options.projectDir, '.feltdb', 'handoff');
144
+ fs.mkdirSync(directory, { recursive: true });
145
+ const jsonPath = path.join(directory, `${investigationId}.agent.json`);
146
+ const markdownPath = path.join(directory, `${investigationId}.agent.md`);
147
+ const markdown = [
148
+ `# FeltDB Investigation ${investigationId}`,
149
+ '',
150
+ '## Observation',
151
+ `${contract.originalObservation.method} ${contract.originalObservation.url} -> HTTP ${contract.originalObservation.status}`,
152
+ `Observed by: ${contract.originalObservation.browser || 'unknown'} (${contract.originalObservation.runtime || 'unknown'})`,
153
+ '',
154
+ '## Evidence (observed by the runtime)',
155
+ ...(investigation.evidence || []).map(item => `- [${item.kind}] ${item.statement}`),
156
+ '',
157
+ '## Finding (FeltDB analysis, not an observation)',
158
+ contract.finding,
159
+ `Confidence: ${investigation.finding?.confidence || 'unknown'}`,
160
+ '',
161
+ '## Relevant source',
162
+ ...(contract.relevantSource.length > 0
163
+ ? contract.relevantSource.map(item => `- ${item.file}${item.line ? `:${item.line}` : ''}`)
164
+ : ['- (no route file matched; locate the handler for this path)']),
165
+ '',
166
+ '## Recommended change',
167
+ ...contract.recommendedChanges.map(item => `- ${item}`),
168
+ '',
169
+ '## Verification contract',
170
+ ...contract.verificationCriteria.map(item => `- ${item.kind}: ${item.method} ${item.url} must return one of [${(item.expectedStatuses || []).join(', ')}]`),
171
+ '',
172
+ 'FeltDB verifies this contract against a real runtime observation after you change the code.',
173
+ 'A passing build, a passing test, or a commit does not satisfy it.',
174
+ ].join('\n');
175
+ fs.writeFileSync(jsonPath, JSON.stringify({
176
+ investigationId,
177
+ workspaceId: investigation.workspaceId,
178
+ // The agent needs to know which repository the source paths are relative
179
+ // to; without it the contract would not be independently actionable.
180
+ repositoryDir: path.resolve(this.options.repositoryDir || this.options.projectDir),
181
+ observation: contract.originalObservation,
182
+ evidence: investigation.evidence || [],
183
+ finding: investigation.finding,
184
+ relevantSource: contract.relevantSource,
185
+ recommendedChanges: contract.recommendedChanges,
186
+ verificationContract: contract.verificationCriteria,
187
+ instructions: agentRemediationHandoff(contract),
188
+ }, null, 2), 'utf8');
189
+ fs.writeFileSync(markdownPath, `${markdown}\n`, 'utf8');
190
+ return { investigationId, contract, markdown, jsonPath, markdownPath };
191
+ }
192
+ /** Whether the repository moved since the investigation baseline. */
193
+ detectRepositoryChange(investigationId) {
194
+ const repositoryDir = this.options.repositoryDir || this.options.projectDir;
195
+ const investigation = this.investigations.loadInvestigation(investigationId);
196
+ if (!investigation)
197
+ throw new Error(`Investigation ${investigationId} not found`);
198
+ const started = investigation.remediationState === 'SENT_TO_AGENT'
199
+ ? this.investigations.markImplementing(investigationId)
200
+ : investigation;
201
+ void started;
202
+ return this.investigations.detectRepositoryChange(investigationId, repositoryDir);
203
+ }
204
+ /**
205
+ * Verify the contract against the runtime.
206
+ *
207
+ * Waits for a matching runtime observation produced after the code change
208
+ * and evaluates it. No observation means INCONCLUSIVE.
209
+ */
210
+ async verify(investigationId, options = { after: 0 }) {
211
+ const investigation = this.investigations.loadInvestigation(investigationId);
212
+ if (!investigation?.remediationContract) {
213
+ throw new Error(`Investigation ${investigationId} has no remediation contract`);
214
+ }
215
+ const criterion = investigation.remediationContract.verificationCriteria.find(item => item.kind === 'primary');
216
+ const observation = await this.awaitObservation(criterion, options);
217
+ return this.investigations.recordVerification(investigationId, investigation.remediationContract, observation);
218
+ }
219
+ /** One-line description of why an investigation exists. */
220
+ describe(investigation) {
221
+ return investigation.originalObservation ? summarizeDefect(investigation.originalObservation) : investigation.id;
222
+ }
223
+ }
224
+ function samePath(actual, expected) {
225
+ try {
226
+ const left = new URL(actual, 'http://localhost');
227
+ const right = new URL(expected, 'http://localhost');
228
+ return left.origin === right.origin && left.pathname === right.pathname && left.search === right.search;
229
+ }
230
+ catch {
231
+ return actual === expected;
232
+ }
233
+ }
234
+ function delay(ms) {
235
+ return new Promise(resolve => setTimeout(resolve, ms));
236
+ }
@@ -5,7 +5,7 @@ export declare function transitionVerification(investigation: RuntimeInvestigati
5
5
  export declare function createRemediationContract(input: Omit<RemediationContract, 'id' | 'observationId' | 'createdAt' | 'updatedAt'>): RemediationContract;
6
6
  export declare function classifyWorkspaceActivity(investigationId: string, paths: string[]): WorkspaceActivity;
7
7
  export declare function evaluateVerification(contract: RemediationContract, observation?: RuntimeRequestObservation, verificationId?: string): VerificationAttempt;
8
- export declare const RUNTIME_INVESTIGATION_INSTRUCTIONS = "Your job:\nInvestigate this runtime observation only.\nDetermine whether it represents an actual defect, identify the most likely root cause, inspect the relevant source, reproduce the behavior when possible, and propose a fix.\nInvestigation is read-only.\nDo NOT:\n- modify source files\n- modify configuration files\n- modify package.json\n- modify .gitignore\n- install dependencies\n- run migrations\n- generate code\n- apply patches\n- commit changes\n- create a PR\n- execute the proposed fix\nYou MAY:\n- inspect source files\n- inspect configuration\n- inspect package scripts\n- inspect logs\n- inspect runtime state\n- run read-only diagnostics\n- reproduce the request\n- run tests that do not mutate the workspace\n- use curl or equivalent HTTP diagnostics\nWhen you have established the cause, stop and report the finding.\nRespond with exactly these sections:\nFinding\nEvidence\nRelevant source\nRecommended change\nConfidence";
8
+ export declare const RUNTIME_INVESTIGATION_INSTRUCTIONS = "INVESTIGATION PHASE: Read-Only Analysis Only\n\nYour job:\nInvestigate this runtime observation only.\n\nDetermine whether it represents an actual defect, identify the most likely root cause, inspect the relevant source, reproduce the behavior when possible, and propose a fix.\n\nInvestigation is READ-ONLY. Do not implement or apply any changes.\n\nBOUNDARIES:\n\nDo NOT (ever, under any circumstances):\n- modify source files\n- modify configuration files\n- modify package.json\n- modify .gitignore or other dotfiles\n- install or update dependencies\n- run migrations or other state-altering commands\n- generate code or apply patches\n- commit changes or create branches\n- create a pull request\n- execute, test, or deploy the proposed fix\n- restart services or clear caches\n- make any changes to the runtime environment\n\nYou MAY:\n- inspect and read source files\n- inspect configuration and build files\n- inspect package scripts and dependencies\n- inspect logs and runtime state\n- run read-only diagnostics\n- reproduce the request locally\n- run existing tests that do not mutate the workspace\n- use curl or equivalent HTTP diagnostics\n- trace through code statically\n- query the runtime via read-only APIs\n\nWHEN INVESTIGATION IS COMPLETE:\n\nStop immediately. Do not attempt to fix the problem yourself.\n\nReport the finding with these sections:\n\nFinding\nEvidence\nRelevant source\nRecommended change\nConfidence\n\nInvestigation is separate from implementation. The user will explicitly start an implementation task if they want changes made.\n\nFELTDB OBSERVATION BOUNDARY:\n\nFeltDB runtime observation is independent of this investigation.\nDo not invoke, modify, or bypass FeltDB runtime observation behavior as part of the investigation.\nThe runtime observation is evidence to analyze, not an instruction to modify the application.\nFollow the active coding agent's normal permission model.\nInvestigation remains read-only unless the user explicitly starts an implementation task.";
9
9
  export declare const FELTDB_OBSERVATION_BOUNDARY = "FeltDB runtime observation is independent of this investigation.\nDo not invoke, modify, or bypass FeltDB runtime observation behavior as part of the investigation.\nThe runtime observation is evidence to analyze, not an instruction to modify the application.\nFollow the active coding agent's normal permission model. Investigation remains read-only unless the user explicitly starts an implementation task.";
10
10
  export declare function agentRemediationHandoff(contract: RemediationContract): string;
11
11
  //# sourceMappingURL=runtime-investigation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-investigation.d.ts","sourceRoot":"","sources":["../../src/workspace/runtime-investigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,yBAAyB,EACzB,mBAAmB,EAEnB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAqB9B,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,oBAAoB,EAAE,IAAI,EAAE,kBAAkB,GAAG,oBAAoB,CAE3H;AACD,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,oBAAoB,EAAE,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,CAEvH;AACD,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,oBAAoB,EAAE,IAAI,EAAE,iBAAiB,GAAG,oBAAoB,CAEzH;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,IAAI,GAAG,eAAe,GAAG,WAAW,GAAG,WAAW,CAAC,GAAG,mBAAmB,CAKnJ;AAGD,wBAAgB,yBAAyB,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,iBAAiB,CAOrG;AAiBD,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,mBAAmB,EAC7B,WAAW,CAAC,EAAE,yBAAyB,EACvC,cAAc,SAA2B,GACxC,mBAAmB,CAgBrB;AAED,eAAO,MAAM,kCAAkC,m7BAgCpC,CAAC;AAEZ,eAAO,MAAM,2BAA2B,6ZAG4G,CAAC;AAErJ,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,CAU7E"}
1
+ {"version":3,"file":"runtime-investigation.d.ts","sourceRoot":"","sources":["../../src/workspace/runtime-investigation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EAChB,oBAAoB,EACpB,yBAAyB,EACzB,mBAAmB,EAEnB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAqB9B,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,oBAAoB,EAAE,IAAI,EAAE,kBAAkB,GAAG,oBAAoB,CAE3H;AACD,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,oBAAoB,EAAE,IAAI,EAAE,gBAAgB,GAAG,oBAAoB,CAEvH;AACD,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,oBAAoB,EAAE,IAAI,EAAE,iBAAiB,GAAG,oBAAoB,CAEzH;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,IAAI,GAAG,eAAe,GAAG,WAAW,GAAG,WAAW,CAAC,GAAG,mBAAmB,CAKnJ;AAGD,wBAAgB,yBAAyB,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,iBAAiB,CAOrG;AAiBD,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,mBAAmB,EAC7B,WAAW,CAAC,EAAE,yBAAyB,EACvC,cAAc,SAA2B,GACxC,mBAAmB,CAgBrB;AAED,eAAO,MAAM,kCAAkC,o+DAyD2C,CAAC;AAE3F,eAAO,MAAM,2BAA2B,6ZAG4G,CAAC;AAErJ,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,CAU7E"}
@@ -78,39 +78,64 @@ export function evaluateVerification(contract, observation, verificationId = uni
78
78
  summary: fixed ? 'The primary verification contract succeeded.' : 'The original defect remains reproducible.',
79
79
  };
80
80
  }
81
- export const RUNTIME_INVESTIGATION_INSTRUCTIONS = `Your job:
81
+ export const RUNTIME_INVESTIGATION_INSTRUCTIONS = `INVESTIGATION PHASE: Read-Only Analysis Only
82
+
83
+ Your job:
82
84
  Investigate this runtime observation only.
85
+
83
86
  Determine whether it represents an actual defect, identify the most likely root cause, inspect the relevant source, reproduce the behavior when possible, and propose a fix.
84
- Investigation is read-only.
85
- Do NOT:
87
+
88
+ Investigation is READ-ONLY. Do not implement or apply any changes.
89
+
90
+ BOUNDARIES:
91
+
92
+ Do NOT (ever, under any circumstances):
86
93
  - modify source files
87
94
  - modify configuration files
88
95
  - modify package.json
89
- - modify .gitignore
90
- - install dependencies
91
- - run migrations
92
- - generate code
93
- - apply patches
94
- - commit changes
95
- - create a PR
96
- - execute the proposed fix
96
+ - modify .gitignore or other dotfiles
97
+ - install or update dependencies
98
+ - run migrations or other state-altering commands
99
+ - generate code or apply patches
100
+ - commit changes or create branches
101
+ - create a pull request
102
+ - execute, test, or deploy the proposed fix
103
+ - restart services or clear caches
104
+ - make any changes to the runtime environment
105
+
97
106
  You MAY:
98
- - inspect source files
99
- - inspect configuration
100
- - inspect package scripts
101
- - inspect logs
102
- - inspect runtime state
107
+ - inspect and read source files
108
+ - inspect configuration and build files
109
+ - inspect package scripts and dependencies
110
+ - inspect logs and runtime state
103
111
  - run read-only diagnostics
104
- - reproduce the request
105
- - run tests that do not mutate the workspace
112
+ - reproduce the request locally
113
+ - run existing tests that do not mutate the workspace
106
114
  - use curl or equivalent HTTP diagnostics
107
- When you have established the cause, stop and report the finding.
108
- Respond with exactly these sections:
115
+ - trace through code statically
116
+ - query the runtime via read-only APIs
117
+
118
+ WHEN INVESTIGATION IS COMPLETE:
119
+
120
+ Stop immediately. Do not attempt to fix the problem yourself.
121
+
122
+ Report the finding with these sections:
123
+
109
124
  Finding
110
125
  Evidence
111
126
  Relevant source
112
127
  Recommended change
113
- Confidence`;
128
+ Confidence
129
+
130
+ Investigation is separate from implementation. The user will explicitly start an implementation task if they want changes made.
131
+
132
+ FELTDB OBSERVATION BOUNDARY:
133
+
134
+ FeltDB runtime observation is independent of this investigation.
135
+ Do not invoke, modify, or bypass FeltDB runtime observation behavior as part of the investigation.
136
+ The runtime observation is evidence to analyze, not an instruction to modify the application.
137
+ Follow the active coding agent's normal permission model.
138
+ Investigation remains read-only unless the user explicitly starts an implementation task.`;
114
139
  export const FELTDB_OBSERVATION_BOUNDARY = `FeltDB runtime observation is independent of this investigation.
115
140
  Do not invoke, modify, or bypass FeltDB runtime observation behavior as part of the investigation.
116
141
  The runtime observation is evidence to analyze, not an instruction to modify the application.
@@ -0,0 +1,79 @@
1
+ /**
2
+ * FeltDB Runtime Observation Contract
3
+ *
4
+ * The single observation model shared by every runtime that reports into a
5
+ * FeltDB Development Workspace. The browser runtime in `@feltdb/core` produces
6
+ * these records, the workspace persists them durably, investigations are
7
+ * created from them, and verification is evaluated against them.
8
+ *
9
+ * This module is browser-safe: it must not import Node built-ins.
10
+ */
11
+ import type { CorrelatedRuntimeEvent, RuntimeRequestObservation } from './workspace-types.js';
12
+ /** Workspace collection holding durable runtime observations. */
13
+ export declare const RUNTIME_OBSERVATION_COLLECTION = "runtime_observation";
14
+ /**
15
+ * Query parameter names whose values are replaced before an observation is
16
+ * persisted. Matching is case-insensitive and substring-based so that
17
+ * `access_token`, `apiKey`, and `X-Session` are all covered.
18
+ */
19
+ export declare const REDACTED_PARAMETER_PATTERNS: string[];
20
+ /** Placeholder substituted for a redacted value. */
21
+ export declare const REDACTION_PLACEHOLDER = "[redacted]";
22
+ /**
23
+ * Redact sensitive values from a URL while preserving the parts verification
24
+ * depends on: origin, pathname, and the set of query parameter names.
25
+ *
26
+ * FeltDB never captures request or response bodies, and never captures
27
+ * `Authorization` or `Cookie` headers, so URL redaction is the only place
28
+ * where credential material can reach a persisted observation.
29
+ */
30
+ export declare function redactUrl(url: string): string;
31
+ /** Redact a free-text runtime message that may embed a URL or credential. */
32
+ export declare function redactMessage(message: string): string;
33
+ /** Browser family and engine derived from a user agent string. */
34
+ export declare function describeBrowser(userAgent: string): {
35
+ browser: string;
36
+ engine: string;
37
+ };
38
+ /**
39
+ * Whether an observation represents an actionable runtime defect.
40
+ *
41
+ * FeltDB does not open an investigation for every request. Only these
42
+ * qualify:
43
+ * - a server-error response (HTTP >= 500)
44
+ * - a request that produced no response at all (network failure)
45
+ * - a client-error response correlated with a runtime or console error
46
+ *
47
+ * Successful requests, redirects, and bare 4xx responses (which are usually
48
+ * intentional application behavior) do not open investigations.
49
+ */
50
+ export declare function isActionableRuntimeDefect(observation: RuntimeRequestObservation): boolean;
51
+ /** Human-readable reason an observation was treated as actionable. */
52
+ export declare function describeDefect(observation: RuntimeRequestObservation): string;
53
+ /** Generate a workspace-unique observation identifier. */
54
+ export declare function createObservationId(): string;
55
+ /** Generate a correlation identifier for a single in-flight request. */
56
+ export declare function createCorrelationId(): string;
57
+ export interface RuntimeObservationInput {
58
+ method: string;
59
+ url: string;
60
+ status: number;
61
+ startedAt: number;
62
+ completedAt: number;
63
+ workspaceId?: string;
64
+ page?: string;
65
+ userAgent?: string;
66
+ correlationId?: string;
67
+ correlatedEvents?: CorrelatedRuntimeEvent[];
68
+ networkFailure?: boolean;
69
+ requestCharacteristics?: Record<string, unknown>;
70
+ responseCharacteristics?: Record<string, unknown>;
71
+ }
72
+ /**
73
+ * Build a redacted runtime observation.
74
+ *
75
+ * All redaction is applied here so that no caller can persist an unredacted
76
+ * observation by accident.
77
+ */
78
+ export declare function createRuntimeObservation(input: RuntimeObservationInput): RuntimeRequestObservation;
79
+ //# sourceMappingURL=runtime-observation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-observation.d.ts","sourceRoot":"","sources":["../../src/workspace/runtime-observation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAE9F,iEAAiE;AACjE,eAAO,MAAM,8BAA8B,wBAAwB,CAAC;AAEpE;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,UAGvC,CAAC;AAEF,oDAAoD;AACpD,eAAO,MAAM,qBAAqB,eAAe,CAAC;AAOlD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAgB7C;AAED,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAatF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,yBAAyB,GAAG,OAAO,CAQzF;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,WAAW,EAAE,yBAAyB,GAAG,MAAM,CAI7E;AAID,0DAA0D;AAC1D,wBAAgB,mBAAmB,IAAI,MAAM,CAG5C;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,sBAAsB,EAAE,CAAC;IAC5C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,sBAAsB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjD,uBAAuB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnD;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,uBAAuB,GAAG,yBAAyB,CAwBlG"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * FeltDB Runtime Observation Contract
3
+ *
4
+ * The single observation model shared by every runtime that reports into a
5
+ * FeltDB Development Workspace. The browser runtime in `@feltdb/core` produces
6
+ * these records, the workspace persists them durably, investigations are
7
+ * created from them, and verification is evaluated against them.
8
+ *
9
+ * This module is browser-safe: it must not import Node built-ins.
10
+ */
11
+ /** Workspace collection holding durable runtime observations. */
12
+ export const RUNTIME_OBSERVATION_COLLECTION = 'runtime_observation';
13
+ /**
14
+ * Query parameter names whose values are replaced before an observation is
15
+ * persisted. Matching is case-insensitive and substring-based so that
16
+ * `access_token`, `apiKey`, and `X-Session` are all covered.
17
+ */
18
+ export const REDACTED_PARAMETER_PATTERNS = [
19
+ 'token', 'key', 'secret', 'password', 'passwd', 'auth', 'session',
20
+ 'credential', 'signature', 'sig', 'code', 'assertion', 'bearer',
21
+ ];
22
+ /** Placeholder substituted for a redacted value. */
23
+ export const REDACTION_PLACEHOLDER = '[redacted]';
24
+ function isRedactedParameter(name) {
25
+ const lowered = name.toLowerCase();
26
+ return REDACTED_PARAMETER_PATTERNS.some(pattern => lowered.includes(pattern));
27
+ }
28
+ /**
29
+ * Redact sensitive values from a URL while preserving the parts verification
30
+ * depends on: origin, pathname, and the set of query parameter names.
31
+ *
32
+ * FeltDB never captures request or response bodies, and never captures
33
+ * `Authorization` or `Cookie` headers, so URL redaction is the only place
34
+ * where credential material can reach a persisted observation.
35
+ */
36
+ export function redactUrl(url) {
37
+ try {
38
+ const parsed = new URL(url, typeof location !== 'undefined' ? location.href : 'http://localhost');
39
+ if (parsed.username || parsed.password) {
40
+ parsed.username = '';
41
+ parsed.password = '';
42
+ }
43
+ for (const name of [...parsed.searchParams.keys()]) {
44
+ if (isRedactedParameter(name))
45
+ parsed.searchParams.set(name, REDACTION_PLACEHOLDER);
46
+ }
47
+ if (parsed.hash)
48
+ parsed.hash = '';
49
+ return parsed.toString();
50
+ }
51
+ catch {
52
+ // A URL we cannot parse is a URL we cannot safely redact.
53
+ return REDACTION_PLACEHOLDER;
54
+ }
55
+ }
56
+ /** Redact a free-text runtime message that may embed a URL or credential. */
57
+ export function redactMessage(message) {
58
+ return message.replace(/https?:\/\/[^\s"'`)]+/g, match => redactUrl(match));
59
+ }
60
+ /** Browser family and engine derived from a user agent string. */
61
+ export function describeBrowser(userAgent) {
62
+ const ua = userAgent || '';
63
+ // Order matters: Brave/Edge/Opera all also claim "Chrome".
64
+ if (/\bBrave\//i.test(ua))
65
+ return { browser: 'Brave', engine: 'Blink' };
66
+ if (/\bEdg\//.test(ua))
67
+ return { browser: 'Edge', engine: 'Blink' };
68
+ if (/\bOPR\//.test(ua))
69
+ return { browser: 'Opera', engine: 'Blink' };
70
+ if (/\bFirefox\//.test(ua))
71
+ return { browser: 'Firefox', engine: 'Gecko' };
72
+ // `HeadlessChrome/` has no word boundary before "Chrome", so match the token
73
+ // directly. Headless Chrome is still Chrome, and still Blink.
74
+ if (/(?:Headless)?Chrome\//.test(ua))
75
+ return { browser: 'Chrome', engine: 'Blink' };
76
+ // Safari identifies itself last because Chromium also carries "Safari".
77
+ if (/\bSafari\//.test(ua) && /\bVersion\//.test(ua))
78
+ return { browser: 'Safari', engine: 'WebKit' };
79
+ return { browser: 'Unknown', engine: 'Unknown' };
80
+ }
81
+ /**
82
+ * Whether an observation represents an actionable runtime defect.
83
+ *
84
+ * FeltDB does not open an investigation for every request. Only these
85
+ * qualify:
86
+ * - a server-error response (HTTP >= 500)
87
+ * - a request that produced no response at all (network failure)
88
+ * - a client-error response correlated with a runtime or console error
89
+ *
90
+ * Successful requests, redirects, and bare 4xx responses (which are usually
91
+ * intentional application behavior) do not open investigations.
92
+ */
93
+ export function isActionableRuntimeDefect(observation) {
94
+ if (observation.networkFailure)
95
+ return true;
96
+ if (observation.status >= 500)
97
+ return true;
98
+ if (observation.status >= 400) {
99
+ return (observation.correlatedEvents || [])
100
+ .some(event => event.kind === 'runtime_error' || event.kind === 'unhandled_rejection');
101
+ }
102
+ return false;
103
+ }
104
+ /** Human-readable reason an observation was treated as actionable. */
105
+ export function describeDefect(observation) {
106
+ if (observation.networkFailure)
107
+ return 'the request produced no HTTP response';
108
+ if (observation.status >= 500)
109
+ return `the endpoint responded with HTTP ${observation.status}`;
110
+ return `the endpoint responded with HTTP ${observation.status} alongside a runtime error`;
111
+ }
112
+ let observationCounter = 0;
113
+ /** Generate a workspace-unique observation identifier. */
114
+ export function createObservationId() {
115
+ observationCounter += 1;
116
+ return `obs_${Date.now()}_${observationCounter}_${Math.random().toString(36).slice(2, 9)}`;
117
+ }
118
+ /** Generate a correlation identifier for a single in-flight request. */
119
+ export function createCorrelationId() {
120
+ return `corr_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
121
+ }
122
+ /**
123
+ * Build a redacted runtime observation.
124
+ *
125
+ * All redaction is applied here so that no caller can persist an unredacted
126
+ * observation by accident.
127
+ */
128
+ export function createRuntimeObservation(input) {
129
+ const { browser, engine } = describeBrowser(input.userAgent || '');
130
+ return {
131
+ observationId: createObservationId(),
132
+ workspaceId: input.workspaceId,
133
+ method: input.method.toUpperCase(),
134
+ url: redactUrl(input.url),
135
+ status: input.status,
136
+ timestamp: input.completedAt,
137
+ durationMs: Math.max(0, input.completedAt - input.startedAt),
138
+ runtime: 'browser',
139
+ browser,
140
+ engine,
141
+ page: input.page ? redactUrl(input.page) : undefined,
142
+ correlationId: input.correlationId,
143
+ correlatedEvents: (input.correlatedEvents || []).map(event => ({
144
+ ...event,
145
+ message: redactMessage(event.message),
146
+ stack: event.stack ? redactMessage(event.stack) : undefined,
147
+ })),
148
+ networkFailure: input.networkFailure || undefined,
149
+ requestCharacteristics: input.requestCharacteristics,
150
+ responseCharacteristics: input.responseCharacteristics,
151
+ };
152
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * FeltDB Browser Runtime Observation
3
+ *
4
+ * The runtime that ships inside `@feltdb/core` and runs in the developer's
5
+ * browser. It instruments the page's HTTP and error surfaces, correlates
6
+ * console/runtime errors to the request that was in flight, and publishes
7
+ * redacted observations into the FeltDB Development Workspace, where they are
8
+ * persisted durably.
9
+ *
10
+ * Browser → @feltdb/core → observation → FeltDB workspace → durable persistence
11
+ *
12
+ * This module is browser-safe: it must not import Node built-ins. It uses only
13
+ * `fetch`, `XMLHttpRequest`, `console`, and `window` event handlers, so the
14
+ * same contract is produced by Chromium, Gecko, and WebKit browsers even
15
+ * though their internal implementations differ.
16
+ */
17
+ import type { RuntimeRequestObservation } from './workspace-types.js';
18
+ export interface RuntimeObservationOptions {
19
+ /** Pairing code (FELT-XXXXXX) printed by `feltdb dev`. */
20
+ pairingCode?: string;
21
+ /** Pairing discovery endpoint, when it differs from the authority. */
22
+ discoveryEndpoint?: string;
23
+ /** Authority endpoint, when the workspace is already known. */
24
+ endpoint?: string;
25
+ /** Workspace id, when the workspace is already known. */
26
+ workspaceId?: string;
27
+ /**
28
+ * How long a failing request is held so that console and runtime errors
29
+ * emitted immediately afterwards can be correlated to it.
30
+ */
31
+ correlationWindowMs?: number;
32
+ /** Expose the handle as `globalThis.__feltdbRuntime`. Defaults to true. */
33
+ exposeGlobal?: boolean;
34
+ }
35
+ export interface RuntimeObservationHandle {
36
+ workspaceId: string;
37
+ /** Observations produced by this page, in order. */
38
+ observations(): RuntimeRequestObservation[];
39
+ /** Resolve once every pending observation has been persisted. */
40
+ flush(): Promise<void>;
41
+ /** Restore the original browser APIs and disconnect from the workspace. */
42
+ stop(): Promise<void>;
43
+ }
44
+ /**
45
+ * Start observing the current page and reporting into the workspace.
46
+ *
47
+ * Returns once the workspace connection is established, so a caller can await
48
+ * it before exercising the application.
49
+ */
50
+ export declare function startRuntimeObservation(options: RuntimeObservationOptions): Promise<RuntimeObservationHandle>;
51
+ //# sourceMappingURL=runtime-observer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-observer.d.ts","sourceRoot":"","sources":["../../src/workspace/runtime-observer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAA0B,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAS9F,MAAM,WAAW,yBAAyB;IACxC,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,2EAA2E;IAC3E,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,oDAAoD;IACpD,YAAY,IAAI,yBAAyB,EAAE,CAAC;IAC5C,iEAAiE;IACjE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,2EAA2E;IAC3E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAID;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,wBAAwB,CAAC,CA0PnC"}