@feltdb/core 0.6.9 → 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.
- package/dist/cli/commands.js +189 -71
- package/dist/cli/index.js +1 -1
- package/dist/collection.d.ts +110 -2
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +228 -3
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +408 -178
- package/dist/create/server-source/crates/feltdb/src/lib.rs +49 -5
- package/dist/create/server-source/crates/feltdb/src/multi_operation_transaction.rs +659 -0
- package/dist/create/server-source/crates/feltdb/src/p1_application_atomicity.rs +285 -0
- package/dist/create/server-source/crates/feltdb/src/p1_atomicity_acceptance.rs +582 -0
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +141 -0
- package/dist/db.d.ts +59 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +94 -2
- package/dist/embedded-transaction.d.ts +77 -0
- package/dist/embedded-transaction.d.ts.map +1 -0
- package/dist/embedded-transaction.js +102 -0
- package/dist/feltdb.d.ts +49 -1
- package/dist/feltdb.d.ts.map +1 -1
- package/dist/file-db.d.ts +33 -0
- package/dist/file-db.d.ts.map +1 -1
- package/dist/file-db.js +128 -4
- package/dist/freshness.d.ts +119 -0
- package/dist/freshness.d.ts.map +1 -0
- package/dist/freshness.js +115 -0
- package/dist/http-db.d.ts +48 -0
- package/dist/http-db.d.ts.map +1 -1
- package/dist/http-db.js +77 -1
- package/dist/index-backend.d.ts +8 -0
- package/dist/index-backend.d.ts.map +1 -1
- package/dist/index-backend.js +13 -0
- package/dist/index-core.d.ts +78 -0
- package/dist/index-core.d.ts.map +1 -0
- package/dist/index-core.js +72 -0
- package/dist/index-manager.d.ts +11 -0
- package/dist/index-manager.d.ts.map +1 -1
- package/dist/index-manager.js +30 -13
- package/dist/index-types.d.ts +17 -0
- package/dist/index-types.d.ts.map +1 -1
- package/dist/index.browser.d.ts +14 -0
- package/dist/index.browser.d.ts.map +1 -0
- package/dist/index.browser.js +13 -0
- package/dist/index.d.ts +8 -59
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -54
- package/dist/indexeddb-db.d.ts +32 -0
- package/dist/indexeddb-db.d.ts.map +1 -1
- package/dist/indexeddb-db.js +151 -1
- package/dist/memory-db.d.ts +26 -0
- package/dist/memory-db.d.ts.map +1 -1
- package/dist/memory-db.js +86 -0
- package/dist/runtime-registry.d.ts +32 -0
- package/dist/runtime-registry.d.ts.map +1 -0
- package/dist/runtime-registry.js +38 -0
- package/dist/studio-app/assets/{feltdb_wasm-B4wq4mqp.js → feltdb_wasm-CJv3wHzi.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-C8TG8r2n.wasm +0 -0
- package/dist/studio-app/assets/index-D3rT0SJi.js +28 -0
- package/dist/studio-app/index.html +1 -1
- package/dist/transaction.d.ts +98 -0
- package/dist/transaction.d.ts.map +1 -0
- package/dist/transaction.js +86 -0
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/dist/workspace/browser.d.ts +22 -0
- package/dist/workspace/browser.d.ts.map +1 -0
- package/dist/workspace/browser.js +17 -0
- package/dist/workspace/git-identity.d.ts +35 -0
- package/dist/workspace/git-identity.d.ts.map +1 -0
- package/dist/workspace/git-identity.js +81 -0
- package/dist/workspace/index.d.ts +11 -1
- package/dist/workspace/index.d.ts.map +1 -1
- package/dist/workspace/index.js +6 -0
- package/dist/workspace/investigation-analysis.d.ts +47 -0
- package/dist/workspace/investigation-analysis.d.ts.map +1 -0
- package/dist/workspace/investigation-analysis.js +181 -0
- package/dist/workspace/investigation-lifecycle-manager.d.ts +86 -0
- package/dist/workspace/investigation-lifecycle-manager.d.ts.map +1 -0
- package/dist/workspace/investigation-lifecycle-manager.js +229 -0
- package/dist/workspace/investigation-supervisor.d.ts +100 -0
- package/dist/workspace/investigation-supervisor.d.ts.map +1 -0
- package/dist/workspace/investigation-supervisor.js +236 -0
- package/dist/workspace/runtime-investigation.d.ts +1 -1
- package/dist/workspace/runtime-investigation.d.ts.map +1 -1
- package/dist/workspace/runtime-investigation.js +46 -21
- package/dist/workspace/runtime-observation.d.ts +79 -0
- package/dist/workspace/runtime-observation.d.ts.map +1 -0
- package/dist/workspace/runtime-observation.js +152 -0
- package/dist/workspace/runtime-observer.d.ts +51 -0
- package/dist/workspace/runtime-observer.d.ts.map +1 -0
- package/dist/workspace/runtime-observer.js +268 -0
- package/dist/workspace/workspace-connection.d.ts.map +1 -1
- package/dist/workspace/workspace-connection.js +14 -5
- package/dist/workspace/workspace-types.d.ts +113 -0
- package/dist/workspace/workspace-types.d.ts.map +1 -1
- package/package.json +12 -4
- package/dist/create/server-source/crates/feltdb/src/phase_1c_three_node.rs +0 -523
- package/dist/studio-app/assets/feltdb_wasm_bg-Ceyi7l21.wasm +0 -0
- 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 = "
|
|
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,
|
|
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 = `
|
|
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
|
-
|
|
85
|
-
Do
|
|
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
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
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
|
-
|
|
108
|
-
|
|
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"}
|