@piagent/platform 1.5.4 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +136 -0
- package/README.md +9 -8
- package/README.vi.md +9 -5
- package/adapters/web-frontend/profile.json +2 -4
- package/architecture/layers.json +18 -3
- package/benchmarks/deep-logic-v1/SPECIALIST_PROTOCOL.vi.md +122 -0
- package/benchmarks/deep-logic-v1/grade.mjs +272 -10
- package/benchmarks/deep-logic-v1/project/apps/admin/src/billing-summary.js +3 -0
- package/benchmarks/deep-logic-v1/project/benchmark-contract/public-smoke.test.js +166 -0
- package/benchmarks/deep-logic-v1/project/package.json +1 -1
- package/benchmarks/deep-logic-v1/project/packages/billing/src/close-period.js +3 -0
- package/benchmarks/deep-logic-v1/project/packages/billing/src/plan-timeline.js +3 -0
- package/benchmarks/deep-logic-v1/project/test/smoke.test.js +7 -1
- package/benchmarks/deep-logic-v1/prompts/budgeted-context-graph.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/fair-dependency-scheduler.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/layered-policy-resolution.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/resumable-stream-assembly.md +3 -1
- package/benchmarks/deep-logic-v1/prompts/revision-event-reconciliation.md +2 -0
- package/benchmarks/deep-logic-v1/prompts/temporal-usage-billing-close.md +123 -0
- package/benchmarks/deep-logic-v1/prompts/transactional-config-merge.md +2 -0
- package/benchmarks/deep-logic-v1/suite.json +51 -3
- package/benchmarks/deep-logic-v1/variant.mjs +22 -1
- package/benchmarks/production-v1/spend-control.v1.json +68 -0
- package/benchmarks/production-v1/suite.json +42 -3
- package/catalog/capabilities.json +6 -6
- package/docs/command-reference-vietnamese.md +6 -6
- package/docs/context-engine.md +7 -1
- package/docs/distribution-standard.md +7 -7
- package/docs/en/architecture.md +1 -0
- package/docs/languages.json +4 -4
- package/docs/model-options.md +8 -5
- package/docs/operator-manual-vietnamese.md +13 -11
- package/docs/project-adapters.md +2 -2
- package/docs/publishing-for-teams.md +9 -0
- package/docs/quality-benchmark.md +271 -62
- package/docs/release-install-policy.md +10 -9
- package/docs/runtime-policy-design.md +4 -4
- package/docs/runtime-quality-baseline.md +13 -6
- package/docs/security-threat-model.md +2 -2
- package/docs/task-lifecycle-vietnamese.md +2 -2
- package/docs/team-onboarding.md +2 -2
- package/docs/usage-observability.md +7 -2
- package/docs/vi/architecture.md +1 -0
- package/docs-site/architecture.html +1 -1
- package/docs-site/benchmark.html +1 -1
- package/docs-site/commands.html +1 -1
- package/docs-site/context-engine.html +1 -1
- package/docs-site/en/architecture.html +1 -1
- package/docs-site/en/benchmark.html +1 -1
- package/docs-site/en/commands.html +1 -1
- package/docs-site/en/context-engine.html +1 -1
- package/docs-site/en/index.html +1 -1
- package/docs-site/en/mcp-approval.html +1 -1
- package/docs-site/en/mcp-auth.html +1 -1
- package/docs-site/en/mcp-servers.html +1 -1
- package/docs-site/en/mcp.html +1 -1
- package/docs-site/en/permissions.html +1 -1
- package/docs-site/en/profiles.html +1 -1
- package/docs-site/en/quickstart.html +1 -1
- package/docs-site/en/runtime.html +1 -1
- package/docs-site/en/scope.html +1 -1
- package/docs-site/en/security.html +1 -1
- package/docs-site/en/team.html +1 -1
- package/docs-site/en/whats-new.html +1 -1
- package/docs-site/en/workflows.html +1 -1
- package/docs-site/index.html +1 -1
- package/docs-site/mcp-approval.html +1 -1
- package/docs-site/mcp-auth.html +1 -1
- package/docs-site/mcp-servers.html +1 -1
- package/docs-site/mcp.html +1 -1
- package/docs-site/permissions.html +1 -1
- package/docs-site/profiles.html +1 -1
- package/docs-site/quickstart.html +1 -1
- package/docs-site/runtime.html +1 -1
- package/docs-site/scope.html +1 -1
- package/docs-site/security.html +1 -1
- package/docs-site/team.html +1 -1
- package/docs-site/whats-new.html +1 -1
- package/docs-site/workflows.html +1 -1
- package/evals/fixtures/benchmark-assurance-evidence.invalid.json +1 -1
- package/evals/fixtures/benchmark-assurance-evidence.valid.json +1 -1
- package/evals/fixtures/piagent-webui/session-live-state-v1.invalid.json +18 -0
- package/evals/fixtures/piagent-webui/session-live-state-v1.valid.json +27 -0
- package/evals/fs4-readiness-matrix.v1.json +2 -2
- package/evals/fs5-causal-arm.v1.json +2 -2
- package/evals/fs5-pilot-protocol.v1.json +2 -2
- package/evals/fs5-pilot-protocol.v4.json +6 -5
- package/evals/fs5-pilot-protocol.v5.json +7 -6
- package/evals/private-holdout-v1/public-exposure.v1.json +1 -1
- package/package.json +3 -1
- package/packages/piagent-core/benchmark/benchmark-assurance.js +4 -1
- package/packages/piagent-core/benchmark/benchmark-bootstrap.js +91 -9
- package/packages/piagent-core/benchmark/benchmark-claim-restrictions.js +38 -7
- package/packages/piagent-core/benchmark/benchmark-cli.js +7 -7
- package/packages/piagent-core/benchmark/benchmark-codex.js +3 -1
- package/packages/piagent-core/benchmark/benchmark-comparison.js +271 -2
- package/packages/piagent-core/benchmark/benchmark-core.js +732 -661
- package/packages/piagent-core/benchmark/benchmark-execution-guard.js +7 -5
- package/packages/piagent-core/benchmark/benchmark-forensics.js +439 -4
- package/packages/piagent-core/benchmark/benchmark-host-readiness.js +639 -0
- package/packages/piagent-core/benchmark/benchmark-matrix.js +22 -4
- package/packages/piagent-core/benchmark/benchmark-normalized-cost.js +317 -0
- package/packages/piagent-core/benchmark/benchmark-preflight.js +3 -1
- package/packages/piagent-core/benchmark/benchmark-process.js +6 -5
- package/packages/piagent-core/benchmark/benchmark-provider-wire.js +126 -0
- package/packages/piagent-core/benchmark/benchmark-record-validation.js +183 -0
- package/packages/piagent-core/benchmark/benchmark-report.js +116 -12
- package/packages/piagent-core/benchmark/benchmark-resume-recovery.js +14 -0
- package/packages/piagent-core/benchmark/benchmark-runner-policy.js +26 -0
- package/packages/piagent-core/benchmark/benchmark-stage-diagnostic.js +782 -0
- package/packages/piagent-core/benchmark/benchmark-statistics.js +64 -0
- package/packages/piagent-core/benchmark/benchmark-stop-policy.js +8 -5
- package/packages/piagent-core/benchmark/benchmark-suite-identity.js +31 -0
- package/packages/piagent-core/benchmark/benchmark-suite-runtime.js +13 -9
- package/packages/piagent-core/benchmark/benchmark-suite.js +128 -2
- package/packages/piagent-core/benchmark/benchmark-summary-support.js +376 -0
- package/packages/piagent-core/benchmark/benchmark-timing-diagnostics.js +971 -0
- package/packages/piagent-core/benchmark/benchmark-usage.js +311 -0
- package/packages/piagent-core/benchmark/benchmark-workflow.js +166 -0
- package/packages/piagent-core/docs/context-efficiency-improvement-plan.md +1080 -0
- package/packages/piagent-core/extensions/acceptance-contract-semantics.js +83 -69
- package/packages/piagent-core/extensions/acceptance-error-classes.js +160 -0
- package/packages/piagent-core/extensions/acceptance-executable-evidence.js +12 -9
- package/packages/piagent-core/extensions/acceptance-receipt.js +34 -11
- package/packages/piagent-core/extensions/acceptance-state-machine-evidence.js +518 -0
- package/packages/piagent-core/extensions/acceptance-test-binding-evidence.js +60 -0
- package/packages/piagent-core/extensions/context-efficiency-metrics.js +302 -0
- package/packages/piagent-core/extensions/context-efficiency-report.js +167 -0
- package/packages/piagent-core/extensions/context-engine.js +148 -145
- package/packages/piagent-core/extensions/context-evidence.js +48 -0
- package/packages/piagent-core/extensions/context-import-links.js +296 -0
- package/packages/piagent-core/extensions/context-telemetry-integrity.js +65 -0
- package/packages/piagent-core/extensions/criterion-context-pack.js +422 -7
- package/packages/piagent-core/extensions/criterion-graph.js +78 -8
- package/packages/piagent-core/extensions/guard-types.ts +3 -0
- package/packages/piagent-core/extensions/javascript-regex-evidence.js +32 -0
- package/packages/piagent-core/extensions/piagent-guard.ts +250 -43
- package/packages/piagent-core/extensions/policy-core.js +1 -1
- package/packages/piagent-core/extensions/task-contract-view.js +17 -3
- package/packages/piagent-core/extensions/task-lifecycle.js +7 -2
- package/packages/piagent-core/extensions/task-state.js +40 -134
- package/packages/piagent-core/extensions/verification-intelligence.js +3 -1
- package/packages/piagent-core/extensions/workspace-evidence-roots.js +157 -0
- package/packages/piagent-core/package.json +1 -1
- package/packages/piagent-core/policies/base-policy.json +3 -0
- package/packages/piagent-core/runtime/context/context-delivery.ts +103 -0
- package/packages/piagent-core/runtime/context/context-delta-shadow.ts +53 -0
- package/packages/piagent-core/runtime/context/context-efficiency-format.ts +47 -0
- package/packages/piagent-core/runtime/context/context-evidence-qualification.ts +32 -0
- package/packages/piagent-core/runtime/context/prefix-telemetry.ts +47 -0
- package/packages/piagent-core/runtime/context/repository-memory-hints.ts +27 -0
- package/packages/piagent-core/runtime/hooks/agent-prompt-telemetry.ts +56 -0
- package/packages/piagent-core/runtime/hooks/agent-start-hook.ts +162 -102
- package/packages/piagent-core/runtime/hooks/completion-hook.ts +5 -6
- package/packages/piagent-core/runtime/hooks/input-hook.ts +11 -1
- package/packages/piagent-core/runtime/hooks/session-hooks.ts +22 -2
- package/packages/piagent-core/runtime/hooks/session-start-hook.ts +7 -2
- package/packages/piagent-core/runtime/hooks/tool-result-content-guards.ts +95 -0
- package/packages/piagent-core/runtime/hooks/tool-result-hook.ts +84 -102
- package/packages/piagent-core/runtime/inspection/activity-event-adapter.ts +9 -3
- package/packages/piagent-core/runtime/inspection/activity-recovery.ts +104 -0
- package/packages/piagent-core/runtime/inspection/approval-broker.ts +29 -5
- package/packages/piagent-core/runtime/inspection/task-compaction-history.ts +1 -1
- package/packages/piagent-core/runtime/inspection/task-handoff-history.ts +1 -1
- package/packages/piagent-core/runtime/inspection/tool-failure-classification.ts +53 -0
- package/packages/piagent-core/runtime/inspection/webui-snapshot.ts +34 -6
- package/packages/piagent-core/runtime/model/authenticated-catalog.ts +17 -9
- package/packages/piagent-core/runtime/model/openai-codex-reasoning.ts +83 -0
- package/packages/piagent-core/runtime/model/provider-wire-fingerprint.ts +238 -0
- package/packages/piagent-core/runtime/orchestration/role-policy.ts +1 -1
- package/packages/piagent-core/runtime/product/activity-inspector.ts +7 -2
- package/packages/piagent-core/runtime/product/efficiency-metrics.ts +4 -2
- package/packages/piagent-core/runtime/quality/exact-output-contract.ts +2 -1
- package/packages/piagent-core/runtime/quality/performance-assurance-signals.ts +23 -0
- package/packages/piagent-core/runtime/quality/performance-assurance.ts +6 -2
- package/packages/piagent-core/runtime/recovery/edit-recovery-context.ts +113 -0
- package/packages/piagent-core/runtime/recovery/edit-recovery-delivery.ts +42 -0
- package/packages/piagent-core/runtime/recovery/semantic-repair-handshake.ts +55 -2
- package/packages/piagent-core/runtime/recovery/semantic-repair-runtime.ts +55 -2
- package/packages/piagent-core/runtime/registration/context-commands.ts +66 -5
- package/packages/piagent-core/runtime/registration/policy-tools.ts +46 -11
- package/packages/piagent-core/runtime/registration/task-completion-tools.ts +49 -16
- package/packages/piagent-core/runtime/registration/task-start-guidance.ts +163 -0
- package/packages/piagent-core/runtime/registration/task-start-retry-helpers.ts +37 -0
- package/packages/piagent-core/runtime/registration/task-start-tool.ts +67 -62
- package/packages/piagent-core/runtime/session/runtime-state.ts +154 -2
- package/packages/piagent-core/runtime/session/system-prompt.ts +4 -4
- package/packages/piagent-core/runtime/tools/apply-patch-tool.ts +500 -0
- package/packages/piagent-core/runtime/trajectory/trajectory-runtime.ts +2 -1
- package/packages/piagent-core/runtime/workflows/task-intake.ts +43 -2
- package/packages/piagent-core/security/sensitive-data.js +13 -218
- package/packages/piagent-core/security/sensitive-project-file.js +42 -0
- package/packages/piagent-core/security/sensitive-source-expression.js +330 -0
- package/packages/piagent-core/security/sensitive-source-formats.js +394 -0
- package/packages/piagent-core/security/sensitive-text.js +234 -0
- package/packages/piagent-core/subagents/piagent-worker.md +1 -1
- package/packages/piagent-webui/dist/client/assets/{MarkdownMessage-rD9WJKxs.js → MarkdownMessage-CxeZqTte.js} +1 -1
- package/packages/piagent-webui/dist/client/assets/index-DynIMe8I.js +176 -0
- package/packages/piagent-webui/dist/client/index.html +1 -1
- package/packages/piagent-webui/gateway/gateway-events.ts +109 -5
- package/packages/piagent-webui/gateway/gateway-service.ts +3 -0
- package/packages/piagent-webui/gateway/gateway-session-stream.ts +93 -7
- package/packages/piagent-webui/gateway/session-command-controller.ts +18 -3
- package/packages/piagent-webui/gateway/session-effective-options.ts +47 -0
- package/packages/piagent-webui/gateway/session-live-state.ts +21 -0
- package/packages/piagent-webui/gateway/session-operation-start.ts +5 -1
- package/packages/piagent-webui/gateway/session-operation-watchdog.ts +158 -0
- package/packages/piagent-webui/gateway/session-runtime-factory.ts +45 -0
- package/packages/piagent-webui/gateway/session-runtime-supervisor.ts +139 -120
- package/packages/piagent-webui/server/loopback-server.ts +8 -0
- package/packages/piagent-webui/server/transcript-projection.ts +22 -4
- package/packages/piagent-webui/shared/text-visibility.ts +10 -0
- package/schemas/piagent-webui/catalog-v1.json +9 -0
- package/schemas/piagent-webui/gateway-protocol-v1.schema.json +53 -3
- package/schemas/piagent-webui/session-live-state-v1.schema.json +76 -0
- package/schemas/task-contract.schema.json +12 -1
- package/scripts/benchmark-runner-core.mjs +355 -359
- package/scripts/benchmark-runner-finalization.mjs +309 -0
- package/scripts/benchmark-runner-host-readiness.mjs +94 -0
- package/scripts/benchmark-runner-support.mjs +200 -0
- package/scripts/benchmark-session.mjs +95 -41
- package/scripts/configure-model-scope.sh +4 -3
- package/scripts/helper-evaluation.mjs +1 -1
- package/scripts/install-global.sh +2 -2
- package/scripts/pi-usage-history.mjs +37 -10
- package/scripts/piagent-dashboard.mjs +13 -1
- package/scripts/setup.sh +2 -2
- package/scripts/verify-local.sh +5 -1
- package/templates/global/settings.json +2 -1
- package/packages/piagent-webui/dist/client/assets/index-BZRFw1u6.js +0 -174
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { regexCanStartAfterLexicalChunks } from "./javascript-regex-evidence.js";
|
|
2
|
+
|
|
3
|
+
function erasedLexeme(value) {
|
|
4
|
+
return String(value ?? "").replace(/[^\r\n]/g, " ");
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const BOUND_IMPORT_PREFIX = "\u0001pi_import_string_";
|
|
8
|
+
const BOUND_IMPORT_SUFFIX = "\u0002";
|
|
9
|
+
|
|
10
|
+
function boundImportString(index) {
|
|
11
|
+
return `${BOUND_IMPORT_PREFIX}${index}${BOUND_IMPORT_SUFFIX}`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function lexicalJavaScriptImportView(value) {
|
|
15
|
+
const source = String(value ?? "");
|
|
16
|
+
const output = [];
|
|
17
|
+
const strings = [];
|
|
18
|
+
let index = 0;
|
|
19
|
+
let scanCode;
|
|
20
|
+
|
|
21
|
+
const scanTemplate = () => {
|
|
22
|
+
const start = index;
|
|
23
|
+
const outputStart = output.length;
|
|
24
|
+
const stringsStart = strings.length;
|
|
25
|
+
output.push("__pi_template_literal__");
|
|
26
|
+
index += 1;
|
|
27
|
+
let rawStart = index;
|
|
28
|
+
while (index < source.length) {
|
|
29
|
+
if (source[index] === "\\") {
|
|
30
|
+
index = Math.min(source.length, index + 2);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (source[index] === "`") {
|
|
34
|
+
output.push(erasedLexeme(source.slice(rawStart, index + 1)));
|
|
35
|
+
index += 1;
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
if (source[index] === "$" && source[index + 1] === "{") {
|
|
39
|
+
output.push(erasedLexeme(source.slice(rawStart, index + 2)));
|
|
40
|
+
output.push("(");
|
|
41
|
+
index += 2;
|
|
42
|
+
if (!scanCode(true) || source[index] !== "}") break;
|
|
43
|
+
output.push(")");
|
|
44
|
+
index += 1;
|
|
45
|
+
rawStart = index;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
index += 1;
|
|
49
|
+
}
|
|
50
|
+
output.splice(outputStart);
|
|
51
|
+
strings.splice(stringsStart);
|
|
52
|
+
output.push(erasedLexeme(source.slice(start, index)));
|
|
53
|
+
return false;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
scanCode = (stopAtTemplateExpression = false) => {
|
|
57
|
+
let braceDepth = 0;
|
|
58
|
+
while (index < source.length) {
|
|
59
|
+
const current = source[index], next = source[index + 1];
|
|
60
|
+
if (stopAtTemplateExpression && current === "}" && braceDepth === 0) return true;
|
|
61
|
+
if (current === "/" && next === "/") {
|
|
62
|
+
let end = index + 2;
|
|
63
|
+
while (end < source.length && !"\r\n".includes(source[end])) end += 1;
|
|
64
|
+
output.push(erasedLexeme(source.slice(index, end)));
|
|
65
|
+
index = end;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (current === "/" && next === "*") {
|
|
69
|
+
const closing = source.indexOf("*/", index + 2);
|
|
70
|
+
const end = closing === -1 ? source.length : closing + 2;
|
|
71
|
+
output.push(erasedLexeme(source.slice(index, end)));
|
|
72
|
+
index = end;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (current === "'" || current === '"') {
|
|
76
|
+
const quote = current;
|
|
77
|
+
let end = index + 1, payload = "", closed = false;
|
|
78
|
+
while (end < source.length) {
|
|
79
|
+
const character = source[end];
|
|
80
|
+
if (character === "\\") {
|
|
81
|
+
payload += source.slice(end, Math.min(source.length, end + 2));
|
|
82
|
+
end += 2;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (character === quote) {
|
|
86
|
+
end += 1;
|
|
87
|
+
closed = true;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
if (character === "\r" || character === "\n") break;
|
|
91
|
+
payload += character;
|
|
92
|
+
end += 1;
|
|
93
|
+
}
|
|
94
|
+
if (!closed) output.push(erasedLexeme(source.slice(index, end)));
|
|
95
|
+
else {
|
|
96
|
+
output.push(boundImportString(strings.length));
|
|
97
|
+
strings.push(payload);
|
|
98
|
+
}
|
|
99
|
+
index = end;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (current === "`") {
|
|
103
|
+
const closed = scanTemplate();
|
|
104
|
+
if (!closed && stopAtTemplateExpression) return false;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (current === "/" && next !== "=" && regexCanStartAfterLexicalChunks(output)) {
|
|
108
|
+
let end = index + 1, inClass = false, closed = false;
|
|
109
|
+
while (end < source.length) {
|
|
110
|
+
const character = source[end];
|
|
111
|
+
if (character === "\\") {
|
|
112
|
+
end += 2;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (character === "[") inClass = true;
|
|
116
|
+
else if (character === "]") inClass = false;
|
|
117
|
+
else if (character === "/" && !inClass) {
|
|
118
|
+
end += 1;
|
|
119
|
+
while (/[a-z]/iu.test(source[end] ?? "")) end += 1;
|
|
120
|
+
closed = true;
|
|
121
|
+
break;
|
|
122
|
+
} else if (character === "\r" || character === "\n") break;
|
|
123
|
+
end += 1;
|
|
124
|
+
}
|
|
125
|
+
if (closed) {
|
|
126
|
+
output.push(erasedLexeme(source.slice(index, end)));
|
|
127
|
+
index = end;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (current === "{") braceDepth += 1;
|
|
132
|
+
else if (current === "}" && braceDepth > 0) braceDepth -= 1;
|
|
133
|
+
output.push(current);
|
|
134
|
+
index += 1;
|
|
135
|
+
}
|
|
136
|
+
return !stopAtTemplateExpression;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
scanCode(false);
|
|
140
|
+
return { code: output.join(""), strings };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const BOUND_IMPORT_STRING = `${BOUND_IMPORT_PREFIX}(\\d+)${BOUND_IMPORT_SUFFIX}`;
|
|
144
|
+
const JAVASCRIPT_IMPORT_PATTERNS = [
|
|
145
|
+
new RegExp(String.raw`(?<![.$\w])(?:import|export)\s+(?:type\s+)?[\w$*{},\s]+?\s+from\s+(${BOUND_IMPORT_STRING})`, "gu"),
|
|
146
|
+
new RegExp(String.raw`(?<![.$\w])import\s+(${BOUND_IMPORT_STRING})`, "gu"),
|
|
147
|
+
new RegExp(String.raw`(?<![.$\w])require\s*\(\s*(${BOUND_IMPORT_STRING})\s*\)`, "gu"),
|
|
148
|
+
new RegExp(String.raw`(?<![.$\w])import\s*\(\s*(${BOUND_IMPORT_STRING})\s*\)`, "gu")
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
function tagEnd(code, start) {
|
|
152
|
+
let braces = 0;
|
|
153
|
+
for (let index = start + 1; index < code.length; index += 1) {
|
|
154
|
+
if (code[index] === "{") braces += 1;
|
|
155
|
+
else if (code[index] === "}") braces = Math.max(0, braces - 1);
|
|
156
|
+
else if (code[index] === ">" && braces === 0) return index + 1;
|
|
157
|
+
}
|
|
158
|
+
return -1;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function jsxTagAt(code, start) {
|
|
162
|
+
if (code[start] !== "<") return undefined;
|
|
163
|
+
const end = tagEnd(code, start);
|
|
164
|
+
if (end === -1) return undefined;
|
|
165
|
+
const raw = code.slice(start, end);
|
|
166
|
+
if (/^<\s*>$/u.test(raw)) return code.slice(end).includes("</>") ? { kind: "open", name: "", end } : undefined;
|
|
167
|
+
if (/^<\/\s*>$/u.test(raw)) return { kind: "close", name: "", end };
|
|
168
|
+
const closing = raw.match(/^<\/\s*([A-Za-z][\w.$:-]*)\s*>$/u);
|
|
169
|
+
if (closing) return { kind: "close", name: closing[1], end };
|
|
170
|
+
const opening = raw.match(/^<\s*([A-Za-z][\w.$:-]*)(?:\s|\/?>)/u);
|
|
171
|
+
if (!opening) return undefined;
|
|
172
|
+
if (/\/\s*>$/u.test(raw)) return { kind: "self", name: opening[1], end };
|
|
173
|
+
const escaped = opening[1].replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
174
|
+
return new RegExp(`<\\/\\s*${escaped}\\s*>`, "u").test(code.slice(end))
|
|
175
|
+
? { kind: "open", name: opening[1], end }
|
|
176
|
+
: undefined;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function jsxTextOffsets(code, offsets) {
|
|
180
|
+
const orderedOffsets = [...new Set(offsets)].sort((left, right) => left - right);
|
|
181
|
+
const textOffsets = new Set();
|
|
182
|
+
const stack = [];
|
|
183
|
+
let braces = 0, index = 0;
|
|
184
|
+
for (const offset of orderedOffsets) {
|
|
185
|
+
while (index < offset) {
|
|
186
|
+
if (code[index] === "<") {
|
|
187
|
+
const tag = jsxTagAt(code, index);
|
|
188
|
+
if (tag && tag.end <= offset) {
|
|
189
|
+
if (tag.kind === "open") stack.push({ name: tag.name, braces });
|
|
190
|
+
else if (tag.kind === "close" && stack.at(-1)?.name === tag.name) stack.pop();
|
|
191
|
+
index = tag.end;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (code[index] === "{") braces += 1;
|
|
196
|
+
else if (code[index] === "}") braces = Math.max(0, braces - 1);
|
|
197
|
+
index += 1;
|
|
198
|
+
}
|
|
199
|
+
if (stack.length > 0 && braces === stack.at(-1).braces) textOffsets.add(offset);
|
|
200
|
+
}
|
|
201
|
+
return textOffsets;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function shadowsCommonJsRequire(code) {
|
|
205
|
+
return /\b(?:class|function|let|const|var)\s+require\b/u.test(code)
|
|
206
|
+
|| /(?<![.$\w])import\s+(?:type\s+)?(?:require\b|\*\s+as\s+require\b|(?:[\w$]+\s*,\s*)?\{[^}\n]*\brequire\b[^}\n]*\})/u.test(code)
|
|
207
|
+
|| /\b(?:function\s*[\w$]*|catch)\s*\([^)]*\brequire\b[^)]*\)/u.test(code)
|
|
208
|
+
|| /(?:\([^)]*\brequire\b[^)]*\)|(?<![.$\w])require)\s*=>/u.test(code)
|
|
209
|
+
|| /\b(?:let|const|var)\s*\{[^}\n]*\brequire\b[^}\n]*\}/u.test(code)
|
|
210
|
+
|| /(?<![.$\w])require\s*(?:=(?!=)|\|\|=|&&=|\?\?=|\*\*=|[+*/%&|^-]=)/u.test(code);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function executableJavaScriptImports(source, lineOffset = 0, { jsx = false } = {}) {
|
|
214
|
+
const { code, strings } = lexicalJavaScriptImportView(source);
|
|
215
|
+
const imports = [];
|
|
216
|
+
const requireShadowed = shadowsCommonJsRequire(code);
|
|
217
|
+
const candidates = [];
|
|
218
|
+
for (const [patternIndex, pattern] of JAVASCRIPT_IMPORT_PATTERNS.entries()) {
|
|
219
|
+
if (patternIndex === 2 && requireShadowed) continue;
|
|
220
|
+
for (const match of code.matchAll(pattern)) {
|
|
221
|
+
candidates.push(match);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const jsxText = jsx ? jsxTextOffsets(code, candidates.map((match) => match.index)) : new Set();
|
|
225
|
+
const pending = [];
|
|
226
|
+
for (const match of candidates) {
|
|
227
|
+
if (jsxText.has(match.index)) continue;
|
|
228
|
+
const stringIndex = Number(match.at(-1));
|
|
229
|
+
const specifier = strings[stringIndex];
|
|
230
|
+
if (typeof specifier !== "string" || !specifier || specifier.length > 300) continue;
|
|
231
|
+
const sentinelOffset = match.index + match[0].lastIndexOf(boundImportString(stringIndex));
|
|
232
|
+
pending.push({ specifier, offset: sentinelOffset });
|
|
233
|
+
}
|
|
234
|
+
const newlines = [];
|
|
235
|
+
for (let offset = code.indexOf("\n"); offset !== -1; offset = code.indexOf("\n", offset + 1)) newlines.push(offset);
|
|
236
|
+
const lineAt = (offset) => {
|
|
237
|
+
let low = 0, high = newlines.length;
|
|
238
|
+
while (low < high) {
|
|
239
|
+
const middle = (low + high) >>> 1;
|
|
240
|
+
if (newlines[middle] < offset) low = middle + 1;
|
|
241
|
+
else high = middle;
|
|
242
|
+
}
|
|
243
|
+
return lineOffset + low + 1;
|
|
244
|
+
};
|
|
245
|
+
for (const candidate of pending) {
|
|
246
|
+
imports.push({ ...candidate, line: lineAt(candidate.offset) });
|
|
247
|
+
}
|
|
248
|
+
return imports;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Extract executable JS/TS module edges without treating comments, examples,
|
|
253
|
+
* string documentation, templates, or regular expressions as live imports.
|
|
254
|
+
*/
|
|
255
|
+
export function extractJavaScriptModuleImports(value, { embedded = false, jsx = false } = {}) {
|
|
256
|
+
const source = String(value ?? "");
|
|
257
|
+
const imports = [];
|
|
258
|
+
if (!embedded) imports.push(...executableJavaScriptImports(source, 0, { jsx }));
|
|
259
|
+
else {
|
|
260
|
+
const visible = source.replace(/<!--[\s\S]*?-->/gu, (comment) => erasedLexeme(comment));
|
|
261
|
+
// HTML end tags may carry whitespace, a self-closing slash, or ignored
|
|
262
|
+
// attributes. Stop at the first complete `script` end tag instead of
|
|
263
|
+
// letting its body absorb later template markup or another script block.
|
|
264
|
+
const script = /^[\t ]*<script(?:>|[\t\n\f\r /][^>]*>)([\s\S]*?)<\/script(?:>|[\t\n\f\r /][^>]*>)/gimu;
|
|
265
|
+
for (const match of visible.matchAll(script)) {
|
|
266
|
+
const content = match[1];
|
|
267
|
+
const contentOffset = match.index + match[0].indexOf(content);
|
|
268
|
+
const lineOffset = visible.slice(0, contentOffset).split(/\r?\n/).length - 1;
|
|
269
|
+
imports.push(...executableJavaScriptImports(content, lineOffset));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return imports
|
|
273
|
+
.sort((left, right) => left.line - right.line || left.offset - right.offset)
|
|
274
|
+
.slice(0, 300)
|
|
275
|
+
.map(({ specifier, line }) => ({ specifier, line }));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function directExplicitImportLinks(importRows, explicitRows) {
|
|
279
|
+
const explicitPaths = new Set(explicitRows.map((row) => row.path));
|
|
280
|
+
const byPath = new Map();
|
|
281
|
+
const rankingRows = [];
|
|
282
|
+
const add = (candidatePath, kind, explicitPath) => {
|
|
283
|
+
const links = byPath.get(candidatePath) ?? [];
|
|
284
|
+
if (!links.some((link) => link.kind === kind && link.path === explicitPath)) {
|
|
285
|
+
links.push({ kind, path: explicitPath });
|
|
286
|
+
rankingRows.push({ path: candidatePath });
|
|
287
|
+
}
|
|
288
|
+
byPath.set(candidatePath, links);
|
|
289
|
+
};
|
|
290
|
+
for (const row of importRows) {
|
|
291
|
+
if (typeof row.specifier !== "string" || !/^\.{1,2}\//.test(row.specifier)) continue;
|
|
292
|
+
if (explicitPaths.has(row.file_path)) add(row.target_path, "explicit-imports-candidate", row.file_path);
|
|
293
|
+
if (explicitPaths.has(row.target_path)) add(row.file_path, "candidate-imports-explicit", row.target_path);
|
|
294
|
+
}
|
|
295
|
+
return { byPath, rankingRows };
|
|
296
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
|
|
3
|
+
import { resolveLocalStatePath } from "./local-state-path.js";
|
|
4
|
+
|
|
5
|
+
const DEFAULT_LIMIT = 50_000;
|
|
6
|
+
const DEFAULT_MAX_BYTES = 32 * 1024 * 1024;
|
|
7
|
+
|
|
8
|
+
function clampInteger(value, fallback, min, max) {
|
|
9
|
+
const number = Number(value);
|
|
10
|
+
if (!Number.isFinite(number)) return fallback;
|
|
11
|
+
return Math.max(min, Math.min(max, Math.trunc(number)));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function readTelemetryFile(filePath, maximumBytes) {
|
|
15
|
+
const descriptor = fs.openSync(filePath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
|
|
16
|
+
try {
|
|
17
|
+
const stat = fs.fstatSync(descriptor);
|
|
18
|
+
if (!stat.isFile()) throw new Error("Context telemetry must be a regular file");
|
|
19
|
+
const start = Math.max(0, stat.size - maximumBytes);
|
|
20
|
+
const buffer = Buffer.allocUnsafe(stat.size - start);
|
|
21
|
+
fs.readSync(descriptor, buffer, 0, buffer.length, start);
|
|
22
|
+
let text = buffer.toString("utf8");
|
|
23
|
+
if (start > 0) text = text.slice(Math.max(0, text.indexOf("\n") + 1));
|
|
24
|
+
return { text, truncated: start > 0 };
|
|
25
|
+
} finally {
|
|
26
|
+
fs.closeSync(descriptor);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Fail-closed JSONL inspection used when telemetry becomes benchmark evidence. */
|
|
31
|
+
export function inspectContextTelemetryIntegrity(projectRoot, telemetryPath, options = {}) {
|
|
32
|
+
const limit = clampInteger(options.limit, DEFAULT_LIMIT, 1, DEFAULT_LIMIT);
|
|
33
|
+
const maximumBytes = clampInteger(options.maxBytes, DEFAULT_MAX_BYTES, 64 * 1024, 256 * 1024 * 1024);
|
|
34
|
+
const records = [];
|
|
35
|
+
let exists = false, integrityFailures = 0, recoverableTailBytes = 0, inputTruncated = false, rotated = false;
|
|
36
|
+
for (const [index, source] of [`${telemetryPath}.1`, telemetryPath].entries()) {
|
|
37
|
+
const safe = resolveLocalStatePath(projectRoot, source, { label: "Context telemetry" });
|
|
38
|
+
try {
|
|
39
|
+
const stat = fs.lstatSync(safe);
|
|
40
|
+
if (!stat.isFile() || stat.isSymbolicLink()) throw new Error("Context telemetry must be a regular file");
|
|
41
|
+
exists = true;
|
|
42
|
+
if (index === 0) rotated = true;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
if (error?.code === "ENOENT") continue;
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
const read = readTelemetryFile(safe, maximumBytes);
|
|
48
|
+
inputTruncated ||= read.truncated;
|
|
49
|
+
const complete = read.text.endsWith("\n") || read.text.endsWith("\r");
|
|
50
|
+
const lines = read.text.split(/\r?\n/);
|
|
51
|
+
if (!complete && lines.at(-1)) recoverableTailBytes += Buffer.byteLength(lines.at(-1));
|
|
52
|
+
for (const [lineIndex, line] of lines.entries()) {
|
|
53
|
+
if (!line) continue;
|
|
54
|
+
try {
|
|
55
|
+
const record = JSON.parse(line);
|
|
56
|
+
if (!record || typeof record !== "object" || Array.isArray(record) || record.truncated === true) integrityFailures += 1;
|
|
57
|
+
else records.push(record);
|
|
58
|
+
} catch {
|
|
59
|
+
if (complete || lineIndex !== lines.length - 1) integrityFailures += 1;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
inputTruncated ||= rotated || records.length > limit;
|
|
64
|
+
return { records: records.slice(-limit), exists, integrityFailures, recoverableTailBytes, inputTruncated };
|
|
65
|
+
}
|