agentera 3.0.0-dev.72 → 3.0.0-dev.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +111 -0
- package/bundle/.agentera-build-source.json +8 -0
- package/bundle/CHANGELOG.md +17 -17
- package/bundle/extract-corpus-parity.json +1 -1
- package/bundle/references/adapters/package-publication.json +49 -53
- package/bundle/references/adapters/package-registry.yaml +5 -1
- package/bundle/references/adapters/package-surface-characterization.md +6 -4
- package/bundle/references/analysis/evidence-tier-authority.yaml +45 -4
- package/bundle/references/analysis/personal-glossary-evaluation-authority.yaml +175 -0
- package/bundle/references/analysis/personal-glossary-evaluation-corpus.yaml +291 -0
- package/bundle/references/analysis/personal-glossary-holdout.yaml +192 -0
- package/bundle/references/analysis/verification-policy.yaml +8 -13
- package/bundle/references/artifacts/glossary-entry-contract.yaml +1533 -35
- package/bundle/references/cli/update-channels.yaml +2 -2
- package/bundle/references/cli/vocabulary.md +1 -1
- package/bundle/references/meta/retained-reference-authority.yaml +16 -1
- package/bundle/skills/agentera/capabilities/profile/schemas/artifacts.yaml +11 -7
- package/bundle/skills/agentera/capabilities/profile/schemas/exit.yaml +6 -3
- package/bundle/skills/agentera/capabilities/profile/schemas/validation.yaml +13 -7
- package/bundle/skills/agentera/schemas/artifacts/glossary.yaml +5 -0
- package/bundle/skills/agentera/schemas/artifacts/plan.yaml +7 -0
- package/dist/.agentera-build-source.json +8 -0
- package/dist/analytics/extractCorpus/copilotSessions.js +13 -4
- package/dist/analytics/extractCorpus/core.js +223 -3
- package/dist/analytics/extractCorpus/corpus.js +18 -1
- package/dist/analytics/extractCorpus/cursorSessions.js +22 -4
- package/dist/analytics/extractCorpus/evidenceTiers.js +123 -17
- package/dist/analytics/extractCorpus/filesystemSources.js +4 -0
- package/dist/analytics/extractCorpus/index.js +2 -2
- package/dist/analytics/extractCorpus/jsonlSessions.js +52 -3
- package/dist/analytics/extractCorpus/sqliteSessions.js +12 -3
- package/dist/analytics/personalGlossaryAdmission.js +26 -46
- package/dist/analytics/personalGlossaryCandidateProjection.js +692 -0
- package/dist/analytics/personalGlossaryCandidateProjectionExcerpts.js +144 -0
- package/dist/analytics/personalGlossaryCandidateReadView.js +23 -0
- package/dist/analytics/personalGlossaryCurrentGeneration.js +25 -0
- package/dist/analytics/personalGlossaryDecision.js +203 -0
- package/dist/analytics/personalGlossaryExplicit.js +788 -0
- package/dist/analytics/personalGlossaryExplicitMining.js +285 -0
- package/dist/analytics/personalGlossaryExplicitScope.js +41 -0
- package/dist/analytics/personalGlossaryExplicitSegments.js +418 -0
- package/dist/analytics/personalGlossaryExplicitTypes.js +26 -0
- package/dist/analytics/personalGlossaryLexicalClassifier.js +175 -0
- package/dist/analytics/personalGlossaryProfile.js +13 -3
- package/dist/analytics/personalGlossaryRecurrence.js +618 -0
- package/dist/analytics/personalGlossaryReviewRecordStorage.js +428 -0
- package/dist/analytics/personalGlossaryReviewRecords.js +332 -0
- package/dist/capabilities/index.js +5 -1
- package/dist/capabilities/profile/instructions.js +50 -15
- package/dist/cli/commands/personalGlossaryCandidateReads.js +744 -0
- package/dist/cli/commands/personalGlossaryDecision.js +349 -0
- package/dist/cli/commands/personalGlossaryPublish.js +484 -0
- package/dist/cli/commands/personalGlossaryReviewRecordReads.js +209 -0
- package/dist/cli/commands/personalGlossaryReviewRecords.js +623 -0
- package/dist/cli/commands/prime/briefOrientation.js +118 -19
- package/dist/cli/commands/report.js +18 -2
- package/dist/cli/commands/schema.js +179 -0
- package/dist/cli/commands/verify.js +51 -9
- package/dist/cli/dispatch/lifecycle.js +51 -6
- package/dist/cli/help.js +48 -2
- package/dist/cli/migrationRequired.js +4 -1
- package/dist/core/atomicWriter.js +4 -1
- package/dist/core/developmentInvocation.js +4 -1
- package/dist/core/yaml.js +53 -1
- package/dist/eval/glossaryEvaluation.js +751 -0
- package/dist/eval/glossaryEvaluationProcess.js +29 -0
- package/dist/eval/glossaryEvaluationRunner.js +571 -0
- package/dist/eval/glossaryEvaluationSuccessReport.js +297 -0
- package/dist/registries/activationTuples.js +31 -3
- package/dist/registries/evidenceTierContract.js +4 -0
- package/dist/registries/explicitSegmentGrammarContract.js +619 -0
- package/dist/registries/glossaryCandidateContractPaths.js +6 -0
- package/dist/registries/glossaryCandidateContracts.js +820 -0
- package/dist/registries/glossaryCandidateDecisionAuthority.js +119 -0
- package/dist/registries/glossaryCandidateDecisionContract.js +62 -0
- package/dist/registries/glossaryCandidateProjectionAuthority.js +197 -0
- package/dist/registries/glossaryCandidateProjectionContract.js +104 -0
- package/dist/registries/glossaryEntryContract.js +105 -103
- package/dist/registries/glossaryEntryTemporal.js +12 -0
- package/dist/registries/glossaryMiningAuthority.js +764 -0
- package/dist/registries/glossaryProfileFullContract.js +24 -0
- package/dist/registries/glossaryReviewRecordsAuthority.js +426 -0
- package/dist/registries/glossaryReviewRecordsContract.js +129 -0
- package/dist/registries/glossaryTermIdentity.js +122 -0
- package/dist/registries/packagePublication.js +3 -3
- package/dist/registries/personalGlossaryContracts.js +1 -0
- package/dist/release/releaseMetadata.js +15 -8
- package/dist/upgrade/npxPlatformStatus.js +2 -1
- package/dist/upgrade/projectIntegration.js +7 -1
- package/dist/validate/activationArtifactEvidence.js +18 -4
- package/dist/validate/activationEvidenceManifest.js +101 -22
- package/package.json +2 -4
- package/dist/cli/commands/personalGlossary.js +0 -176
|
@@ -3,7 +3,7 @@ status: active_authority
|
|
|
3
3
|
purpose: >-
|
|
4
4
|
Define dual update channels that select which published Agentera line upgrade,
|
|
5
5
|
doctor, and prime resolve: stable tracks the supported 2.x line; development
|
|
6
|
-
tracks 3.x
|
|
6
|
+
tracks 3.x prereleases.
|
|
7
7
|
sources:
|
|
8
8
|
prose:
|
|
9
9
|
- references/cli/vocabulary.md#update-channels
|
|
@@ -60,7 +60,7 @@ channels:
|
|
|
60
60
|
development:
|
|
61
61
|
concept: v3_pre_release_line
|
|
62
62
|
definition: >-
|
|
63
|
-
Tracks 3.x
|
|
63
|
+
Tracks 3.x prereleases for early adopters and
|
|
64
64
|
maintainers testing the npm self-contained TypeScript distribution. The
|
|
65
65
|
feat/v3 branch and v3 CLI are npm-only; v2→v3 is a one-way upgrade through
|
|
66
66
|
npx @next, not through uvx or git checkout installs.
|
|
@@ -399,7 +399,7 @@ for upgrade, doctor, prime, docs, and tests.
|
|
|
399
399
|
|
|
400
400
|
Use this prose as guidance only: **stable** tracks the supported 2.x npm line
|
|
401
401
|
and, for maintainers, `uvx --from git+...@main` (Python
|
|
402
|
-
CLI). **development** tracks 3.x
|
|
402
|
+
CLI). **development** tracks 3.x prereleases on **npm only**
|
|
403
403
|
(`npx -y agentera@next`); feat/v3 is TypeScript-only and has no uv/git install
|
|
404
404
|
path. v2→v3 is a one-way upgrade through the development npm channel. Default
|
|
405
405
|
channel is stable. Override with `--channel`, `AGENTERA_UPDATE_CHANNEL`, or
|
|
@@ -19,6 +19,21 @@ inventory:
|
|
|
19
19
|
production_owner: { module: packages/cli/src/registries/evidenceTierContract.ts, symbol: loadEvidenceTierContract }
|
|
20
20
|
consumers:
|
|
21
21
|
- { kind: runtime, module: packages/cli/src/registries/evidenceTierContract.ts, symbol: loadEvidenceTierContract, consumption: loads }
|
|
22
|
+
- path: references/analysis/personal-glossary-evaluation-authority.yaml
|
|
23
|
+
classification: current
|
|
24
|
+
production_owner: { module: packages/cli/src/eval/glossaryEvaluation.ts, symbol: loadGlossaryEvaluationAuthority }
|
|
25
|
+
consumers:
|
|
26
|
+
- { kind: runtime, module: packages/cli/src/eval/glossaryEvaluationSuccessReport.ts, symbol: validateGlossaryEvaluationSuccessReport, consumption: loads }
|
|
27
|
+
- path: references/analysis/personal-glossary-holdout.yaml
|
|
28
|
+
classification: current
|
|
29
|
+
production_owner: { module: packages/cli/src/eval/glossaryEvaluation.ts, symbol: loadGlossaryEvaluationHoldout }
|
|
30
|
+
consumers:
|
|
31
|
+
- { kind: runtime, module: packages/cli/src/eval/glossaryEvaluationSuccessReport.ts, symbol: validateGlossaryEvaluationSuccessReport, consumption: loads }
|
|
32
|
+
- path: references/analysis/personal-glossary-evaluation-corpus.yaml
|
|
33
|
+
classification: current
|
|
34
|
+
production_owner: { module: packages/cli/src/eval/glossaryEvaluation.ts, symbol: loadGlossaryEvaluationBehaviorFixture }
|
|
35
|
+
consumers:
|
|
36
|
+
- { kind: runtime, module: packages/cli/src/eval/glossaryEvaluationSuccessReport.ts, symbol: validateGlossaryEvaluationSuccessReport, consumption: loads }
|
|
22
37
|
- path: references/analysis/verification-policy.yaml
|
|
23
38
|
classification: current
|
|
24
39
|
production_owner: { module: packages/cli/scripts/verify-lane.mjs, symbol: loadContract }
|
|
@@ -65,7 +80,7 @@ inventory:
|
|
|
65
80
|
classification: current
|
|
66
81
|
production_owner: { module: packages/cli/src/release/releaseMetadata.ts, symbol: readReleaseMetadata }
|
|
67
82
|
consumers:
|
|
68
|
-
- { kind: validator, module: packages/cli/src/release/releaseMetadata.ts, symbol:
|
|
83
|
+
- { kind: validator, module: packages/cli/src/release/releaseMetadata.ts, symbol: readUpdateChannels, consumption: loads }
|
|
69
84
|
- path: references/cli/vocabulary-index.yaml
|
|
70
85
|
classification: current
|
|
71
86
|
production_owner: { module: packages/cli/src/validate/vocabularyAuthority.ts, symbol: validateVocabularyAuthority }
|
|
@@ -5,13 +5,17 @@ ARTIFACTS:
|
|
|
5
5
|
local_role: produces_and_consumes
|
|
6
6
|
description: >-
|
|
7
7
|
Decision profile with per-entry confidence scoring, permanence
|
|
8
|
-
classification, and temporal metadata.
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
classification, and temporal metadata. After writing the base profile,
|
|
9
|
+
Profile Full consumes one existing consent-bound user-local personal
|
|
10
|
+
glossary generation. Only complete current coverage reaches bounded
|
|
11
|
+
classification and current explicit CLI decisions can replace its
|
|
12
|
+
deterministic owned Glossary section; inferred or ambiguous candidates
|
|
13
|
+
use the bounded review lifecycle. Unavailable, stale, or degraded coverage
|
|
14
|
+
preserves the base and owned section and requires explicit refresh. Lookup remains deferred. Profile writes
|
|
15
|
+
this in both Full and Validate modes, then reads it for validation and
|
|
16
|
+
update context. Prime exposes its canonical resolved path only inside
|
|
17
|
+
`prime --context profile`; no other Prime context or profile-grounding
|
|
18
|
+
failure may serialize that path.
|
|
15
19
|
2:
|
|
16
20
|
id: A2
|
|
17
21
|
artifact: decisions
|
|
@@ -4,8 +4,10 @@ EXIT_CONDITIONS:
|
|
|
4
4
|
condition: complete
|
|
5
5
|
description: >-
|
|
6
6
|
PROFILE.md was written (Full mode) or updated (Validate mode).
|
|
7
|
-
In Full mode
|
|
8
|
-
|
|
7
|
+
In Full mode the base profile was written first, then any existing personal
|
|
8
|
+
glossary generation produced only current authorized explicit publication;
|
|
9
|
+
remaining review work was queued or bounded for a question channel.
|
|
10
|
+
Metadata changes were applied, prediction accuracy was assessed, and
|
|
9
11
|
changes were summarized.
|
|
10
12
|
exit_signal: complete
|
|
11
13
|
2:
|
|
@@ -16,7 +18,8 @@ EXIT_CONDITIONS:
|
|
|
16
18
|
issues: available runtime history was skipped without explicit
|
|
17
19
|
acceptance, extraction failed for one or more sources, prediction
|
|
18
20
|
accuracy was below 3/5, or significant unresolved tensions were
|
|
19
|
-
found.
|
|
21
|
+
found. Full mode is also flagged when its existing consent-bound personal
|
|
22
|
+
glossary generation is unavailable, stale, degraded, or review-only.
|
|
20
23
|
exit_signal: flagged
|
|
21
24
|
3:
|
|
22
25
|
id: E3
|
|
@@ -87,17 +87,22 @@ VALIDATION:
|
|
|
87
87
|
id: V7
|
|
88
88
|
rule: personal_glossary_output
|
|
89
89
|
description: >-
|
|
90
|
-
Profile Full MUST
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
90
|
+
After writing the base profile, Profile Full MUST consume only one existing
|
|
91
|
+
consent-bound user-local generation. The canonical publication operation
|
|
92
|
+
may render and persist exactly one owned Glossary section only after current
|
|
93
|
+
decision revalidation, and only for an explicit automatic admission.
|
|
94
|
+
Entries use only the shared primitive and personal provenance variants.
|
|
95
|
+
Replacement preserves every non-glossary byte, malformed or ambiguous
|
|
96
|
+
boundaries fail before effects, and personal production never reads a
|
|
97
|
+
project glossary.
|
|
95
98
|
severity: critical
|
|
96
99
|
checks:
|
|
97
|
-
- "Explicit
|
|
98
|
-
- "Inferred entries
|
|
100
|
+
- "Explicit publication retains exactly one current user-authored evidence anchor"
|
|
101
|
+
- "Inferred entries cannot receive automatic publication"
|
|
99
102
|
- "Non-glossary PROFILE.md bytes and decision-pattern confidence are unchanged"
|
|
100
103
|
- "No project glossary identity, path, parameter, import, or read is used"
|
|
104
|
+
- "Missing, failed, empty, degraded, or review-only mining preserves the base and owned section"
|
|
105
|
+
- "Degraded coverage performs no candidate classification, decision, review queue, or publication"
|
|
101
106
|
8:
|
|
102
107
|
id: V8
|
|
103
108
|
rule: personal_glossary_lifecycle
|
|
@@ -113,3 +118,4 @@ VALIDATION:
|
|
|
113
118
|
- "Stable, durable, and situational classes use their existing lambdas and floor"
|
|
114
119
|
- "Fresh evidence refreshes only matching meaning and provenance kind"
|
|
115
120
|
- "Glossary confidence never mutates decision-pattern confidence"
|
|
121
|
+
- "Question-capable hosts present only a bounded queued review set; other hosts use the durable queue"
|
|
@@ -19,6 +19,11 @@ CONFORMANCE:
|
|
|
19
19
|
ownership_contract: project
|
|
20
20
|
provenance_variant: project_file
|
|
21
21
|
term_identity_runtime: packages/cli/src/registries/glossaryTermIdentity.ts#unicodeCaselessExact
|
|
22
|
+
personal_mining_authority: references/artifacts/glossary-entry-contract.yaml#personal_mining_authority
|
|
23
|
+
stable_term_identity_runtime: packages/cli/src/registries/glossaryTermIdentity.ts#stableGlossaryTermIdentity
|
|
24
|
+
candidate_revision_runtime: packages/cli/src/registries/glossaryTermIdentity.ts#glossaryCandidateRevision
|
|
25
|
+
candidate_contracts_authority: references/artifacts/glossary-entry-contract.yaml#candidate_contracts
|
|
26
|
+
candidate_contracts_runtime: packages/cli/src/registries/glossaryCandidateContracts.ts#validatePersonalCandidateContracts
|
|
22
27
|
term_occurrence_runtime: packages/cli/src/registries/glossaryTermOccurrence.ts#containsGlossaryTerm
|
|
23
28
|
|
|
24
29
|
DOCUMENT:
|
|
@@ -623,6 +623,13 @@ LIFECYCLE_CONTRACT:
|
|
|
623
623
|
roots:
|
|
624
624
|
- .
|
|
625
625
|
symlinks: Excluded because their targets may leave the repository boundary.
|
|
626
|
+
generated_output:
|
|
627
|
+
owner: packages/cli/scripts/generated-output.mjs#generatedOutputRoot
|
|
628
|
+
package_roots: [packages/cli]
|
|
629
|
+
treatment: exclude_exact_generated_root_subtree
|
|
630
|
+
reason: >-
|
|
631
|
+
Generated verification and build evidence is retained for bounded
|
|
632
|
+
downstream readers, but is not a tracked lifecycle-bearing source surface.
|
|
626
633
|
excluded_directory_names:
|
|
627
634
|
names:
|
|
628
635
|
- .git
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "agentera.generatedBuildSource.v1",
|
|
3
|
+
"commit": "01a0c460f2310bae5805a637973021af30960ee5",
|
|
4
|
+
"tree": "998eef2c833de05d61612ce021a02ecb28a0b2b5",
|
|
5
|
+
"files": 6748,
|
|
6
|
+
"workingTreeSha256": "3c780bac24a26ed1c40735614d2e9313eb30114901d6b71beb427a74e144bb22",
|
|
7
|
+
"identitySha256": "04d4e2a388eb6420f37ce0ee4c7052d3a85035d2e0e23e4eaaa980b96e36bf58"
|
|
8
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import { resolvePath } from "../../core/paths.js";
|
|
3
|
-
import { isoFromMtime, record, signalType, textFromContent, } from "./core.js";
|
|
3
|
+
import { authorClassForRole, isoFromMtime, record, signalType, textFromContent, transportProvenance, } from "./core.js";
|
|
4
4
|
import { isPlainObject, rglob, isFilePath } from "./core.js";
|
|
5
5
|
import { resolveSqliteCaps, } from "./sqliteCaps.js";
|
|
6
|
-
import { PermissionDeniedError, openSqlite, sqliteTimestamp, tableColumns, firstColumn, qualified, } from "./sqliteSessions.js";
|
|
6
|
+
import { PermissionDeniedError, openSqlite, jsonDict, sqliteTimestamp, tableColumns, firstColumn, qualified, } from "./sqliteSessions.js";
|
|
7
7
|
function copilotDbCandidates(storePath) {
|
|
8
8
|
if (isFilePath(storePath))
|
|
9
9
|
return [storePath];
|
|
@@ -208,15 +208,18 @@ export function extractCopilotSessions(storePath, errors, ctx) {
|
|
|
208
208
|
let index = 0;
|
|
209
209
|
for (const row of rows) {
|
|
210
210
|
index += 1;
|
|
211
|
-
|
|
212
|
-
if (role !== "user" && role !== "assistant")
|
|
211
|
+
if (typeof row.role !== "string" || row.role.length === 0)
|
|
213
212
|
continue;
|
|
213
|
+
const role = row.role.toLowerCase();
|
|
214
214
|
const content = textFromContent(row.turn_text);
|
|
215
215
|
const toolItems = copilotRowTools(row, errors);
|
|
216
216
|
if (!content && toolItems.length === 0)
|
|
217
217
|
continue;
|
|
218
218
|
const projectPath = row.project_path ? String(row.project_path) : null;
|
|
219
219
|
const timestamp = sqliteTimestamp(row.turn_time || row.session_time, fallbackTimestamp);
|
|
220
|
+
const transport = transportProvenance(row, jsonDict(row.turn_data));
|
|
221
|
+
const originId = transport.originId;
|
|
222
|
+
const authorClass = authorClassForRole(role, transport.authorClass, transport.originId);
|
|
220
223
|
if (content) {
|
|
221
224
|
const data = { actor: role, content };
|
|
222
225
|
if (role === "user") {
|
|
@@ -237,6 +240,9 @@ export function extractCopilotSessions(storePath, errors, ctx) {
|
|
|
237
240
|
sourceProduct: "github-copilot",
|
|
238
241
|
sourceParts: [resolvePath(dbPath), index, role, content.slice(0, 80)],
|
|
239
242
|
sessionId: String(row.session_id),
|
|
243
|
+
originId,
|
|
244
|
+
authorClass,
|
|
245
|
+
content,
|
|
240
246
|
data,
|
|
241
247
|
}));
|
|
242
248
|
}
|
|
@@ -265,6 +271,9 @@ export function extractCopilotSessions(storePath, errors, ctx) {
|
|
|
265
271
|
sourceProduct: "github-copilot",
|
|
266
272
|
sourceParts: [resolvePath(dbPath), index, "history", content.slice(0, 120)],
|
|
267
273
|
sessionId: String(row.session_id),
|
|
274
|
+
originId,
|
|
275
|
+
authorClass,
|
|
276
|
+
content,
|
|
268
277
|
data: { prompt: content, signal_type: sig },
|
|
269
278
|
}));
|
|
270
279
|
}
|
|
@@ -5,7 +5,7 @@ import { resolveProfileDirOverride, resolveXdgDataHome } from "../../core/envPat
|
|
|
5
5
|
import { expanduser } from "../../core/paths.js";
|
|
6
6
|
import crypto from "node:crypto";
|
|
7
7
|
import { resolvePath } from "../../core/paths.js";
|
|
8
|
-
export const ADAPTER_VERSION = "agentera-v3-corpus-
|
|
8
|
+
export const ADAPTER_VERSION = "agentera-v3-corpus-3";
|
|
9
9
|
export const FAMILIES = [
|
|
10
10
|
"instruction_document",
|
|
11
11
|
"history_prompt",
|
|
@@ -27,6 +27,19 @@ export const MAX_TOOL_ARG_TEXT = 500;
|
|
|
27
27
|
export const MAX_SQLITE_ROWS = 100_000;
|
|
28
28
|
export const MAX_SQLITE_SESSIONS = 60;
|
|
29
29
|
export const COPILOT_SPARSE_REMEDIATION = "/chronicle reindex";
|
|
30
|
+
const ORIGIN_ID_RE = /^[a-f0-9]{64}$/;
|
|
31
|
+
const CONTENT_FINGERPRINT_RE = /^[a-f0-9]{64}$/;
|
|
32
|
+
const CONVERSATION_SOURCE_KINDS = new Set(["conversation_turn", "history_prompt"]);
|
|
33
|
+
const PROVENANCE_NESTED_KEYS = ["payload", "item", "metadata", "meta", "provenance", "source"];
|
|
34
|
+
const PROVENANCE_ORIGIN_KEYS = [
|
|
35
|
+
"origin_id",
|
|
36
|
+
"original_origin_id",
|
|
37
|
+
"original_origin",
|
|
38
|
+
"origin",
|
|
39
|
+
"source_origin",
|
|
40
|
+
"instruction_origin",
|
|
41
|
+
];
|
|
42
|
+
const PROVENANCE_AUTHOR_KEYS = ["author_class", "authorClass", "author"];
|
|
30
43
|
const DECISION_RE = /\b(decide|decision|prefer|preference|instead|avoid|don't|do not|should|trade[- ]?off|scope|plan|commit|review|fix|why|question|blocked|stuck|approve|reject|change|keep|remove)\b/i;
|
|
31
44
|
const CORRECTION_RE = /\b(no|not quite|actually|rather|instead|wrong|correction|that's not|that is not|don't|do not)\b/i;
|
|
32
45
|
const QUESTION_RE = /\?|^\s*(why|what|how|should|can|could|would)\b/i;
|
|
@@ -44,6 +57,195 @@ export function stableId(...parts) {
|
|
|
44
57
|
const raw = parts.map((p) => pyStr(p)).join("\0");
|
|
45
58
|
return crypto.createHash("sha256").update(raw, "utf-8").digest("hex").slice(0, 24);
|
|
46
59
|
}
|
|
60
|
+
/** A bounded, deterministic identity for the source that originally supplied a record. */
|
|
61
|
+
export function originIdentity(origin) {
|
|
62
|
+
if (typeof origin !== "string" || origin.length === 0) {
|
|
63
|
+
throw new TypeError("origin must be a non-empty string");
|
|
64
|
+
}
|
|
65
|
+
return crypto
|
|
66
|
+
.createHash("sha256")
|
|
67
|
+
.update("agentera.corpus.origin.v1\0", "utf-8")
|
|
68
|
+
.update(origin, "utf-8")
|
|
69
|
+
.digest("hex");
|
|
70
|
+
}
|
|
71
|
+
/** A lowercase SHA-256 of the exact source text, without normalization or truncation. */
|
|
72
|
+
export function contentFingerprint(content) {
|
|
73
|
+
if (typeof content !== "string")
|
|
74
|
+
throw new TypeError("content must be a string");
|
|
75
|
+
return crypto.createHash("sha256").update(content, "utf-8").digest("hex");
|
|
76
|
+
}
|
|
77
|
+
function nonEmptyString(value) {
|
|
78
|
+
return typeof value === "string" && value.length > 0;
|
|
79
|
+
}
|
|
80
|
+
function provenanceObjects(sources) {
|
|
81
|
+
const out = [];
|
|
82
|
+
const pending = sources.slice();
|
|
83
|
+
const seen = new Set();
|
|
84
|
+
while (pending.length > 0 && out.length < 24) {
|
|
85
|
+
const value = pending.shift();
|
|
86
|
+
if (!isPlainObject(value) || seen.has(value))
|
|
87
|
+
continue;
|
|
88
|
+
seen.add(value);
|
|
89
|
+
out.push(value);
|
|
90
|
+
for (const key of PROVENANCE_NESTED_KEYS) {
|
|
91
|
+
const nested = value[key];
|
|
92
|
+
if (isPlainObject(nested))
|
|
93
|
+
pending.push(nested);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
function inspectStringField(sources, keys) {
|
|
99
|
+
for (const source of sources) {
|
|
100
|
+
for (const key of keys) {
|
|
101
|
+
if (!Object.prototype.hasOwnProperty.call(source, key))
|
|
102
|
+
continue;
|
|
103
|
+
const value = source[key];
|
|
104
|
+
return nonEmptyString(value)
|
|
105
|
+
? { state: "valid", key, value }
|
|
106
|
+
: { state: "invalid", key, value: null };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return { state: "absent", key: null, value: null };
|
|
110
|
+
}
|
|
111
|
+
function transportOriginId(key, value) {
|
|
112
|
+
return ORIGIN_ID_RE.test(value) && key.endsWith("_id") ? value : originIdentity(value);
|
|
113
|
+
}
|
|
114
|
+
/** Inspect the first explicit transport fields without collapsing malformed values into absence. */
|
|
115
|
+
export function inspectTransportProvenance(...sources) {
|
|
116
|
+
const objects = provenanceObjects(sources);
|
|
117
|
+
const origin = inspectStringField(objects, PROVENANCE_ORIGIN_KEYS);
|
|
118
|
+
const author = inspectStringField(objects, PROVENANCE_AUTHOR_KEYS);
|
|
119
|
+
return {
|
|
120
|
+
originId: origin.state === "valid" ? transportOriginId(origin.key, origin.value) : null,
|
|
121
|
+
authorClass: author.state === "valid" ? author.value : null,
|
|
122
|
+
originState: origin.state,
|
|
123
|
+
authorState: author.state,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Read only explicit transport metadata. Content is never inspected to infer
|
|
128
|
+
* either ownership or origin. An adapter may provide an origin digest directly,
|
|
129
|
+
* or a bounded source label that is converted to one.
|
|
130
|
+
*/
|
|
131
|
+
export function transportProvenance(...sources) {
|
|
132
|
+
let origin = null;
|
|
133
|
+
let authorClass = null;
|
|
134
|
+
for (const source of provenanceObjects(sources)) {
|
|
135
|
+
if (origin === null) {
|
|
136
|
+
for (const key of PROVENANCE_ORIGIN_KEYS) {
|
|
137
|
+
const value = source[key];
|
|
138
|
+
if (nonEmptyString(value)) {
|
|
139
|
+
origin = transportOriginId(key, value);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (authorClass === null) {
|
|
145
|
+
for (const key of PROVENANCE_AUTHOR_KEYS) {
|
|
146
|
+
const value = source[key];
|
|
147
|
+
if (nonEmptyString(value)) {
|
|
148
|
+
authorClass = value;
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (origin !== null && authorClass !== null)
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
return { originId: origin, authorClass };
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Map a transport role to a source-author class without examining its text.
|
|
160
|
+
* A claimed transported origin requires the original author to be explicit;
|
|
161
|
+
* the envelope role is not evidence for the source that was transported.
|
|
162
|
+
*/
|
|
163
|
+
export function authorClassForRole(role, explicitAuthorClass = null, transportedOriginId = null) {
|
|
164
|
+
const explicit = nonEmptyString(explicitAuthorClass);
|
|
165
|
+
if (!explicit && nonEmptyString(transportedOriginId))
|
|
166
|
+
return null;
|
|
167
|
+
const value = explicit ? explicitAuthorClass : nonEmptyString(role) ? role : null;
|
|
168
|
+
if (value === null)
|
|
169
|
+
return null;
|
|
170
|
+
switch (value.toLowerCase()) {
|
|
171
|
+
case "user":
|
|
172
|
+
return "user";
|
|
173
|
+
case "assistant":
|
|
174
|
+
case "model":
|
|
175
|
+
case "tool":
|
|
176
|
+
return "agent";
|
|
177
|
+
case "system":
|
|
178
|
+
case "developer":
|
|
179
|
+
case "instruction":
|
|
180
|
+
return "injected_instruction";
|
|
181
|
+
default:
|
|
182
|
+
return explicit ? value : null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/** Return the bounded provenance fields missing from one retained record. */
|
|
186
|
+
export function missingProvenanceFields(item) {
|
|
187
|
+
const missing = [];
|
|
188
|
+
const required = [
|
|
189
|
+
["source_id", nonEmptyString(item.source_id)],
|
|
190
|
+
["source_kind", nonEmptyString(item.source_kind)],
|
|
191
|
+
["timestamp", nonEmptyString(item.timestamp)],
|
|
192
|
+
["project_id", nonEmptyString(item.project_id)],
|
|
193
|
+
["source_class", nonEmptyString(item.source_class)],
|
|
194
|
+
["source_product", nonEmptyString(item.source_product)],
|
|
195
|
+
["adapter_version", nonEmptyString(item.adapter_version)],
|
|
196
|
+
["data", isPlainObject(item.data)],
|
|
197
|
+
];
|
|
198
|
+
for (const [field, present] of required)
|
|
199
|
+
if (!present)
|
|
200
|
+
missing.push(field);
|
|
201
|
+
if (typeof item.runtime !== "string" && item.runtime !== null)
|
|
202
|
+
missing.push("runtime");
|
|
203
|
+
if (typeof item.active_runtime !== "boolean")
|
|
204
|
+
missing.push("active_runtime");
|
|
205
|
+
if (CONVERSATION_SOURCE_KINDS.has(String(item.source_kind))) {
|
|
206
|
+
if (!ORIGIN_ID_RE.test(String(item.origin_id ?? "")))
|
|
207
|
+
missing.push("origin_id");
|
|
208
|
+
if (!nonEmptyString(item.author_class))
|
|
209
|
+
missing.push("author_class");
|
|
210
|
+
if (!CONTENT_FINGERPRINT_RE.test(String(item.content_fingerprint ?? ""))) {
|
|
211
|
+
missing.push("content_fingerprint");
|
|
212
|
+
}
|
|
213
|
+
if (!nonEmptyString(item.session_id))
|
|
214
|
+
missing.push("session_id");
|
|
215
|
+
}
|
|
216
|
+
return [...new Set(missing)];
|
|
217
|
+
}
|
|
218
|
+
export function assessProvenance(records) {
|
|
219
|
+
const fields = new Set();
|
|
220
|
+
let missingRecords = 0;
|
|
221
|
+
for (const record of records) {
|
|
222
|
+
const missing = missingProvenanceFields(record);
|
|
223
|
+
if (missing.length === 0)
|
|
224
|
+
continue;
|
|
225
|
+
missingRecords += 1;
|
|
226
|
+
for (const field of missing)
|
|
227
|
+
fields.add(field);
|
|
228
|
+
}
|
|
229
|
+
return { complete: missingRecords === 0, missingFields: [...fields].sort(), missingRecords };
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Count independent conversation origins without using record IDs or copy
|
|
233
|
+
* counts. Repeated transport of one origin and fingerprint contributes once;
|
|
234
|
+
* a different origin remains independent even when its text is identical.
|
|
235
|
+
*/
|
|
236
|
+
export function countIndependentOrigins(records) {
|
|
237
|
+
const identities = new Set();
|
|
238
|
+
for (const record of records) {
|
|
239
|
+
if (!CONVERSATION_SOURCE_KINDS.has(String(record.source_kind)))
|
|
240
|
+
continue;
|
|
241
|
+
const originId = record.origin_id;
|
|
242
|
+
const fingerprint = record.content_fingerprint;
|
|
243
|
+
if (!ORIGIN_ID_RE.test(String(originId ?? "")) || !CONTENT_FINGERPRINT_RE.test(String(fingerprint ?? "")))
|
|
244
|
+
continue;
|
|
245
|
+
identities.add(`${originId}\0${fingerprint}`);
|
|
246
|
+
}
|
|
247
|
+
return identities.size;
|
|
248
|
+
}
|
|
47
249
|
/** Python str() for the scalar/None values stable_id receives. */
|
|
48
250
|
function pyStr(value) {
|
|
49
251
|
if (value === null || value === undefined)
|
|
@@ -112,6 +314,12 @@ export function runtimeStatus(runtime, opts) {
|
|
|
112
314
|
item.truncation_cap = opts.truncationCap;
|
|
113
315
|
if (opts.truncationLimit !== null && opts.truncationLimit !== undefined)
|
|
114
316
|
item.truncation_limit = opts.truncationLimit;
|
|
317
|
+
if (opts.provenanceMissingFields && opts.provenanceMissingFields.length > 0) {
|
|
318
|
+
item.provenance_missing_fields = opts.provenanceMissingFields;
|
|
319
|
+
}
|
|
320
|
+
if (opts.provenanceMissingRecords !== null && opts.provenanceMissingRecords !== undefined) {
|
|
321
|
+
item.provenance_missing_records = opts.provenanceMissingRecords;
|
|
322
|
+
}
|
|
115
323
|
return item;
|
|
116
324
|
}
|
|
117
325
|
function isDir(p) {
|
|
@@ -184,7 +392,7 @@ export function discoverRuntimeStore(runtime, storePath, opts = {}) {
|
|
|
184
392
|
});
|
|
185
393
|
}
|
|
186
394
|
if (!isDir(storePath)) {
|
|
187
|
-
return runtimeStatus(runtime, { status: "degraded", reason: "store_not_directory", storePath });
|
|
395
|
+
return runtimeStatus(runtime, { status: "degraded", reason: "store_not_directory", storePath, ...statusOpts });
|
|
188
396
|
}
|
|
189
397
|
let candidates;
|
|
190
398
|
try {
|
|
@@ -194,7 +402,7 @@ export function discoverRuntimeStore(runtime, storePath, opts = {}) {
|
|
|
194
402
|
candidates = rglob(storePath, pattern);
|
|
195
403
|
}
|
|
196
404
|
catch {
|
|
197
|
-
return runtimeStatus(runtime, { status: "degraded", reason: "store_unreadable", storePath });
|
|
405
|
+
return runtimeStatus(runtime, { status: "degraded", reason: "store_unreadable", storePath, ...statusOpts });
|
|
198
406
|
}
|
|
199
407
|
if (candidates.length === 0) {
|
|
200
408
|
return runtimeStatus(runtime, {
|
|
@@ -241,6 +449,16 @@ export function record(opts) {
|
|
|
241
449
|
item.session_id = opts.sessionId;
|
|
242
450
|
item.conversation_key = opts.sessionId;
|
|
243
451
|
}
|
|
452
|
+
if (opts.originId)
|
|
453
|
+
item.origin_id = opts.originId;
|
|
454
|
+
else if (opts.origin)
|
|
455
|
+
item.origin_id = originIdentity(opts.origin);
|
|
456
|
+
else if (opts.sessionId)
|
|
457
|
+
item.origin_id = originIdentity(`session:${opts.sessionId}`);
|
|
458
|
+
if (opts.authorClass)
|
|
459
|
+
item.author_class = opts.authorClass;
|
|
460
|
+
if (typeof opts.content === "string")
|
|
461
|
+
item.content_fingerprint = contentFingerprint(opts.content);
|
|
244
462
|
return item;
|
|
245
463
|
}
|
|
246
464
|
function isPlainObject(v) {
|
|
@@ -383,6 +601,8 @@ export function toolCallRecordFromItem(args) {
|
|
|
383
601
|
activeRuntime: args.activeRuntime,
|
|
384
602
|
sourceParts: [resolvePath(args.sourcePath), args.index, "tool", toolName],
|
|
385
603
|
sessionId: args.sessionId,
|
|
604
|
+
originId: transportProvenance(args.event, args.item).originId,
|
|
605
|
+
authorClass: "agent",
|
|
386
606
|
data: { tool_name: toolName, arguments: argumentsVal }, // cast: parsed JSON IO boundary
|
|
387
607
|
});
|
|
388
608
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import { ADAPTER_VERSION, FAMILIES, isoNow, runtimeStatus, discoverRuntimeStore, COPILOT_SPARSE_REMEDIATION, } from "./core.js";
|
|
2
|
+
import { ADAPTER_VERSION, FAMILIES, isoNow, runtimeStatus, discoverRuntimeStore, COPILOT_SPARSE_REMEDIATION, assessProvenance, } from "./core.js";
|
|
3
3
|
import { isPlainObject } from "./core.js";
|
|
4
4
|
import { extractInstructionDocuments, extractProjectConfigSignals } from "./filesystemSources.js";
|
|
5
5
|
import { extractCodexSessions, extractClaudeProjectSessions } from "./jsonlSessions.js";
|
|
@@ -36,6 +36,23 @@ function extractRuntimeStore(runtime, storePath, errors, extractor, sqliteCaps,
|
|
|
36
36
|
}
|
|
37
37
|
const fc = (discovery.file_count ?? null); // cast: discovery payload IO boundary
|
|
38
38
|
const errorCount = errors.length - errorStart;
|
|
39
|
+
const recordProvenance = assessProvenance(records);
|
|
40
|
+
if (!recordProvenance.complete) {
|
|
41
|
+
return [
|
|
42
|
+
records,
|
|
43
|
+
runtimeStatus(runtime, {
|
|
44
|
+
status: "degraded",
|
|
45
|
+
reason: "provenance_missing",
|
|
46
|
+
storePath,
|
|
47
|
+
fileCount: fc,
|
|
48
|
+
recordCount: records.length,
|
|
49
|
+
errorCount,
|
|
50
|
+
provenanceMissingFields: recordProvenance.missingFields,
|
|
51
|
+
provenanceMissingRecords: recordProvenance.missingRecords,
|
|
52
|
+
...provenance,
|
|
53
|
+
}),
|
|
54
|
+
];
|
|
55
|
+
}
|
|
39
56
|
if (errorCount) {
|
|
40
57
|
return [records, runtimeStatus(runtime, { status: "degraded", reason: "schema_divergent", storePath, fileCount: fc, recordCount: records.length, errorCount, ...provenance })];
|
|
41
58
|
}
|
|
@@ -4,7 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import crypto from "node:crypto";
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
6
6
|
import { expanduser, resolvePath } from "../../core/paths.js";
|
|
7
|
-
import { MAX_TOOL_ARG_TEXT, eventTimestamp, isFilePath, isoFromMtime, iterJsonl, record, signalType, textFromContent, toolCallRecordFromItem, } from "./core.js";
|
|
7
|
+
import { authorClassForRole, MAX_TOOL_ARG_TEXT, eventTimestamp, isFilePath, isoFromMtime, iterJsonl, record, signalType, textFromContent, transportProvenance, toolCallRecordFromItem, } from "./core.js";
|
|
8
8
|
import { isPlainObject, rglob, isDir } from "./core.js";
|
|
9
9
|
import { openSqlite, } from "./sqliteSessions.js";
|
|
10
10
|
function pathStem(p) {
|
|
@@ -153,7 +153,7 @@ export function extractCursorSessions(projectsDir, errors, projectRoots = null)
|
|
|
153
153
|
for (const event of iterJsonl(p, errors)) {
|
|
154
154
|
index += 1;
|
|
155
155
|
const role = event.role;
|
|
156
|
-
if (role !== "
|
|
156
|
+
if (typeof role !== "string" || role.length === 0)
|
|
157
157
|
continue;
|
|
158
158
|
let contentIndex = 0;
|
|
159
159
|
for (const item of cursorContentItems(event)) {
|
|
@@ -180,6 +180,9 @@ export function extractCursorSessions(projectsDir, errors, projectRoots = null)
|
|
|
180
180
|
if (!content)
|
|
181
181
|
continue;
|
|
182
182
|
const timestamp = eventTimestamp(event, fallbackTimestamp);
|
|
183
|
+
const transport = transportProvenance(event, ...items);
|
|
184
|
+
const originId = transport.originId;
|
|
185
|
+
const authorClass = authorClassForRole(role, transport.authorClass, transport.originId);
|
|
183
186
|
const data = { actor: role, content };
|
|
184
187
|
if (role === "user") {
|
|
185
188
|
if (previousAssistant)
|
|
@@ -198,6 +201,9 @@ export function extractCursorSessions(projectsDir, errors, projectRoots = null)
|
|
|
198
201
|
runtime: "cursor",
|
|
199
202
|
sourceParts: [resolvePath(p), index, role, content.slice(0, 80)],
|
|
200
203
|
sessionId,
|
|
204
|
+
originId,
|
|
205
|
+
authorClass,
|
|
206
|
+
content,
|
|
201
207
|
data,
|
|
202
208
|
}));
|
|
203
209
|
if (role === "user") {
|
|
@@ -210,6 +216,9 @@ export function extractCursorSessions(projectsDir, errors, projectRoots = null)
|
|
|
210
216
|
runtime: "cursor",
|
|
211
217
|
sourceParts: [resolvePath(p), index, "history", content.slice(0, 120)],
|
|
212
218
|
sessionId,
|
|
219
|
+
originId,
|
|
220
|
+
authorClass,
|
|
221
|
+
content,
|
|
213
222
|
data: { prompt: content, signal_type: sig },
|
|
214
223
|
}));
|
|
215
224
|
}
|
|
@@ -356,6 +365,8 @@ export function extractCursorAgentSessions(chatsDir, errors, projectRoots = null
|
|
|
356
365
|
for (const [blobId, message] of cursorAgentBlobMessages(storeDb, errors)) {
|
|
357
366
|
index += 1;
|
|
358
367
|
const role = message.role;
|
|
368
|
+
if (typeof role !== "string" || role.length === 0)
|
|
369
|
+
continue;
|
|
359
370
|
let toolIndex = 0;
|
|
360
371
|
for (const toolItem of cursorAgentToolItems(message)) {
|
|
361
372
|
toolIndex += 1;
|
|
@@ -377,12 +388,13 @@ export function extractCursorAgentSessions(chatsDir, errors, projectRoots = null
|
|
|
377
388
|
records.push(tr);
|
|
378
389
|
}
|
|
379
390
|
}
|
|
380
|
-
if (role !== "user" && role !== "assistant")
|
|
381
|
-
continue;
|
|
382
391
|
const content = cursorAgentMessageText(message);
|
|
383
392
|
if (!content)
|
|
384
393
|
continue;
|
|
385
394
|
const timestamp = eventTimestamp(message, fallbackTimestamp);
|
|
395
|
+
const transport = transportProvenance(message);
|
|
396
|
+
const originId = transport.originId;
|
|
397
|
+
const authorClass = authorClassForRole(role, transport.authorClass, transport.originId);
|
|
386
398
|
const data = { actor: role, content };
|
|
387
399
|
if (role === "user") {
|
|
388
400
|
if (previousAssistant)
|
|
@@ -402,6 +414,9 @@ export function extractCursorAgentSessions(chatsDir, errors, projectRoots = null
|
|
|
402
414
|
sourceProduct: "cursor-agent",
|
|
403
415
|
sourceParts: [resolvePath(storeDb), blobId, role, content.slice(0, 80)],
|
|
404
416
|
sessionId,
|
|
417
|
+
originId,
|
|
418
|
+
authorClass,
|
|
419
|
+
content,
|
|
405
420
|
data,
|
|
406
421
|
}));
|
|
407
422
|
if (role === "user") {
|
|
@@ -415,6 +430,9 @@ export function extractCursorAgentSessions(chatsDir, errors, projectRoots = null
|
|
|
415
430
|
sourceProduct: "cursor-agent",
|
|
416
431
|
sourceParts: [resolvePath(storeDb), blobId, "history", content.slice(0, 120)],
|
|
417
432
|
sessionId,
|
|
433
|
+
originId,
|
|
434
|
+
authorClass,
|
|
435
|
+
content,
|
|
418
436
|
data: { prompt: content, signal_type: sig },
|
|
419
437
|
}));
|
|
420
438
|
}
|