agent-workflow-inspector 0.0.0-stage → 1.0.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 +13 -0
- package/CODE_OF_CONDUCT.md +5 -0
- package/CONTRIBUTING.md +9 -0
- package/LICENSE +21 -0
- package/README.md +95 -2
- package/SECURITY.md +5 -0
- package/awi.config.example.json +34 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +1179 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +232 -0
- package/dist/index.js +1071 -0
- package/dist/index.js.map +1 -0
- package/docs/architecture.md +21 -0
- package/docs/audit.md +91 -0
- package/docs/ci-integration.md +17 -0
- package/docs/configuration.md +15 -0
- package/docs/detection-rules.md +28 -0
- package/docs/getting-started.md +29 -0
- package/docs/limitations.md +11 -0
- package/docs/releases/v1.0.0.md +32 -0
- package/docs/releasing.md +112 -0
- package/docs/report-schema.md +22 -0
- package/docs/schemas/config.schema.json +145 -0
- package/docs/schemas/report.schema.json +117 -0
- package/docs/schemas/trace.schema.json +98 -0
- package/docs/security.md +11 -0
- package/docs/trace-formats.md +41 -0
- package/docs/troubleshooting.md +12 -0
- package/docs/verification.md +38 -0
- package/examples/healthy/basic.json +30 -0
- package/examples/opentelemetry/tools.json +210 -0
- package/examples/suspicious/cycle.json +78 -0
- package/examples/suspicious/retries.json +116 -0
- package/package.json +77 -3
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# V1.0.0 release procedure
|
|
2
|
+
|
|
3
|
+
This is an approval-gated plan, not an automated publisher. All public actions below are pending explicit maintainer approval. No publish workflow or write credential has been added. Run commands from the repository root in PowerShell, check each exit code, and stop on any failure or unexpected remote state. GitHub CLI (`gh`) and a supported npm CLI must be installed/authenticated before publication; neither is currently available/authenticated in the Windows release shell. Never print credentials or copy them into the repository.
|
|
4
|
+
|
|
5
|
+
## Candidate and repository state
|
|
6
|
+
|
|
7
|
+
- Package: `agent-workflow-inspector@1.0.0`, MIT. Source bin `dist/cli/index.js`, ESM export and declarations `dist/index.js` / `dist/index.d.ts`; Node 22.18+ (22.x), 24.11+ (24.x), 26.x.
|
|
8
|
+
- Local `main` preserves audited history through d2514af. The feature branch is retained. No force push, history rewrite, branch deletion or tag creation is required during preparation.
|
|
9
|
+
- At inspection, public origin was `https://github.com/jigar48/awi.git`, default branch `codex/awi-v1`, sole remote commit d2514af, with no tags/releases. Description/homepage were empty and topics were empty.
|
|
10
|
+
- Public branch metadata reported `protected:false`; repository rulesets returned an empty list. The detailed protection endpoint returned 403 to the connected integration, so a maintainer must confirm settings before changing defaults. Do not bypass any rules; use the required review path if protection changes.
|
|
11
|
+
- The npm registry returned 404 on October 19, 2026. The name is not reserved and no ownership is established by that result. Linux npm 11.17.0 `whoami` returned ENEEDAUTH; Windows npm is absent from PATH. Authenticate the intended maintainer account, with 2FA, and recheck availability immediately before publishing. Never overwrite an existing version; if 1.0.0 appears, stop and compare ownership/source before choosing a new version.
|
|
12
|
+
- Historical [CI run](https://github.com/jigar48/awi/actions/runs/39208844432) passed all nine jobs for d2514af only. It does not validate this later candidate.
|
|
13
|
+
|
|
14
|
+
## Ordered public actions — approval required
|
|
15
|
+
|
|
16
|
+
### 1. Push only the final main candidate
|
|
17
|
+
|
|
18
|
+
```powershell
|
|
19
|
+
git status --porcelain=v1
|
|
20
|
+
git switch main
|
|
21
|
+
$releaseSha = (git rev-parse HEAD).Trim()
|
|
22
|
+
git merge-base --is-ancestor d2514af main
|
|
23
|
+
git ls-remote --symref origin
|
|
24
|
+
git push --set-upstream origin main
|
|
25
|
+
git ls-remote origin refs/heads/main
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Require a clean tree, the approved SHA and preserved audited ancestry. If remote main appears or changes, fetch and inspect ancestry before pushing; never force. This push triggers `.github/workflows/ci.yml` on main without changing the default branch.
|
|
29
|
+
|
|
30
|
+
### 2. Verify fresh main CI
|
|
31
|
+
|
|
32
|
+
```powershell
|
|
33
|
+
gh run list --repo jigar48/awi --workflow ci.yml --branch main --commit $releaseSha --event push --json databaseId,headSha,status,conclusion,url
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Select the matching run ID, then run `gh run watch RUN_ID --repo jigar48/awi --exit-status` and `gh run view RUN_ID --repo jigar48/awi --json headSha,conclusion,jobs`. Require exactly nine successful verify jobs: Windows, macOS and Ubuntu × Node 22.18.0, 24.11.0 and 26. Every install/test/type/lint/build/format/schema/audit/pack/consumer step must pass. Stop publication on a failure or SHA mismatch. Any correction requires a new commit, push, matrix and package validation.
|
|
37
|
+
|
|
38
|
+
### 3. Set default branch and prepared metadata
|
|
39
|
+
|
|
40
|
+
Only after successful main CI, inspect branch settings with an admin identity. Apply the prepared description/homepage/topics and default branch:
|
|
41
|
+
|
|
42
|
+
```powershell
|
|
43
|
+
gh api --method PATCH repos/jigar48/awi --input .github/repository-metadata.json
|
|
44
|
+
gh api --method PUT repos/jigar48/awi/topics --input .github/repository-topics.json
|
|
45
|
+
gh repo edit jigar48/awi --default-branch main
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Topics satisfy GitHub's current rules: eight lowercase names using letters/hyphens, each at most 50 characters; maximum allowed is 20. See [GitHub topic requirements](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/classifying-your-repository-with-topics). Funding appears from `.github/FUNDING.yml`; no external account settings are changed by the local file.
|
|
49
|
+
|
|
50
|
+
### 4. Freeze artifact and create annotated tag
|
|
51
|
+
|
|
52
|
+
Recheck HEAD equals the successful CI SHA and clean status. Preserve the exact locally validated tarball and its external SHA-512 integrity manifest. Do not rebuild or substitute it after approval; if regenerated, repeat the clean consumer check and recompute the manifest. Inspect `git ls-remote --tags origin` and ensure no v1.0.0 exists.
|
|
53
|
+
|
|
54
|
+
```powershell
|
|
55
|
+
git tag -a v1.0.0 $releaseSha -m 'Agent Workflow Inspector v1.0.0'
|
|
56
|
+
git push origin refs/tags/v1.0.0
|
|
57
|
+
git rev-parse 'v1.0.0^{}'
|
|
58
|
+
git ls-remote origin 'refs/tags/v1.0.0*'
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Require the peeled remote tag to equal `$releaseSha`. A tag push also triggers CI; do not confuse it with the already required main run. Do not move an existing tag.
|
|
62
|
+
|
|
63
|
+
### 5. Publish GitHub release
|
|
64
|
+
|
|
65
|
+
The prepared notes use absolute version-tagged documentation URLs. Remove the candidate status line only in the temporary publication body after approval; preserve the source notes as a preparation record.
|
|
66
|
+
|
|
67
|
+
```powershell
|
|
68
|
+
$notes = Get-Content docs/releases/v1.0.0.md -Raw
|
|
69
|
+
$notes = $notes.Replace('Release notes prepared for approval. This version has not been published.', 'Initial public release.')
|
|
70
|
+
[IO.File]::WriteAllText((Join-Path $PWD '.awi-audit/release-body.md'), $notes)
|
|
71
|
+
gh release create v1.0.0 agent-workflow-inspector-1.0.0.tgz .awi-audit/release-candidate.json --repo jigar48/awi --verify-tag --title 'Agent Workflow Inspector v1.0.0 — Deterministic AI Agent Trace Diagnostics' --notes-file .awi-audit/release-body.md
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The release body labels npm installation as conditional until registry publication succeeds, so an npm authentication failure does not falsely advertise availability. No GitHub release has been created during preparation.
|
|
75
|
+
|
|
76
|
+
### 6. Publish npm securely
|
|
77
|
+
|
|
78
|
+
For the first version, prefer interactive npm web login/2FA on the maintainer's trusted machine. The current npm [trust CLI prerequisites](https://docs.npmjs.com/cli/v11/commands/npm-trust/) require the package to exist before a trusted publisher can be configured. Therefore this unpublished name cannot yet use that setup path. Do not create a bootstrap/dummy version, or a long-lived npm automation token.
|
|
79
|
+
|
|
80
|
+
Install a current supported npm CLI and use the intended maintainer account. Commands below are pending approval and human authentication:
|
|
81
|
+
|
|
82
|
+
```powershell
|
|
83
|
+
npm login --auth-type=web --registry=https://registry.npmjs.org
|
|
84
|
+
npm whoami --registry=https://registry.npmjs.org
|
|
85
|
+
npm view agent-workflow-inspector versions maintainers --json --registry=https://registry.npmjs.org
|
|
86
|
+
npm publish ./agent-workflow-inspector-1.0.0.tgz --access public --registry=https://registry.npmjs.org --ignore-scripts
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Expected pre-publication registry response is E404. Any other response needs review; if version 1.0.0 exists, stop. Human authentication is intentionally outside agent logs. Publish the already validated tarball with lifecycle scripts disabled. Local interactive publication does not create GitHub OIDC provenance; do not claim it does.
|
|
90
|
+
|
|
91
|
+
GitHub-hosted OIDC trusted publishing is suitable for subsequent versions: public repository/package, pinned actions, protected manual publish workflow, explicit immutable source SHA/tag, passing CI gate, protected environment, `contents: read` and job-scoped `id-token: write`, no write token or release-build cache. npm documents Node >=22.14 and npm >=11.5.1 for OIDC; the trust CLI needs npm >=11.15. A future reviewed `publish.yml` must exist before npm configuration names it. Configure `jigar48/awi`, that exact workflow filename and environment in package settings; stage-only permission with maintainer 2FA approval is preferred. Current npm documentation also requires a new trusted configuration's first successful publish within two days. See [trusted publishing](https://docs.npmjs.com/trusted-publishers/). Do not enable any publisher or add a live workflow during this preparation.
|
|
92
|
+
|
|
93
|
+
### 7. Verify registry and clean registry consumer
|
|
94
|
+
|
|
95
|
+
```powershell
|
|
96
|
+
npm view agent-workflow-inspector@1.0.0 name version license engines bin exports types repository homepage bugs dist --json --registry=https://registry.npmjs.org
|
|
97
|
+
npm owner ls agent-workflow-inspector --registry=https://registry.npmjs.org
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Require registry `dist.integrity` to equal `.awi-audit/release-candidate.json` tarballIntegrity. Download the registry tarball, verify its SHA-512 and compare bytes with the approved artifact. Never accept matching version strings alone. The tarball manifest records the source SHA; the annotated tag and successful CI must identify that same SHA. Reject any payload difference.
|
|
101
|
+
|
|
102
|
+
Create a new temporary directory outside the checkout, `npm init -y`, then `npm install --ignore-scripts --save-exact agent-workflow-inspector@1.0.0 --registry=https://registry.npmjs.org`. Execute its installed `node_modules/.bin/awi` (`awi.cmd` on Windows) with --help, --version and all four shipped examples using resolved package paths; expected results match `scripts/package-smoke.mjs`. Check JSON output, exit codes, ESM public imports and strict TypeScript imports, using only declared consumer tooling. Clean up the temporary directory after inspection. This registry-based check cannot run before publication; the local tarball equivalent has already run.
|
|
103
|
+
|
|
104
|
+
### 8. Verify release links and record outcome
|
|
105
|
+
|
|
106
|
+
Use `gh release view v1.0.0 --repo jigar48/awi --json url,tagName,targetCommitish,assets,body` and repository metadata to confirm the default main branch, tag, notes and downloadable artifact. Open version-tagged documentation URLs and npm package page. Match the release tarball to the same SHA-512 manifest and record the successful main run URL and release commit. A registry/release mismatch is a release incident, not grounds to force-move a tag or overwrite a version.
|
|
107
|
+
|
|
108
|
+
After successful publication, a separate documentation-only follow-up may replace the README's candidate notice, add the actual publication date to the changelog, and record registry/release URLs and integrity. Such a follow-up is not part of the v1.0.0 source tag and must not be represented as its published contents.
|
|
109
|
+
|
|
110
|
+
## Approval gate
|
|
111
|
+
|
|
112
|
+
Local preparation is complete only after full validation and tarball consumer smoke pass. Publication remains blocked on approval, fresh main CI and authenticated npm ownership/name checks. Approval must explicitly cover main push, remote metadata/default-branch changes, annotated tag push, GitHub release and npm publication. The previous feature-branch push authorization does not authorize these actions.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# JSON report v1
|
|
2
|
+
|
|
3
|
+
[Machine schema](schemas/report.schema.json). Fields: version 1, fingerprintVersion awi-sha256-v1, complete, summaries, findings, warnings. No clock or absolute paths. Optional content requires explicit opt-in.
|
|
4
|
+
|
|
5
|
+
Summary fields: traceId, operations, toolCalls, maxDepth, optional durationMs and recorded leaf usage. Unknown resources stay absent. Finding fields: ruleId, severity, confidence, title, description, explanation, remediation, traceId, operationIds, structured evidence. References contain operationId and adapter source format/index when available. traceId + operationId is authoritative across merged files; source index preserves adapter recorded order, not causal order. AWI005 is trace-wide with no individual operation list.
|
|
6
|
+
|
|
7
|
+
Findings sort by descending severity, trace ID, rule ID, then operation ID list using lexical comparison. Warnings deduplicate and sort. CLI filesystem inputs enumerate deterministically. Operations and resource evidence use stable ID ordering after ingestion; timing rules use exact timestamps. Adapter source indices retain recorded order, so ingesting a reordered raw export changes those evidence indices deliberately. Direct-library trace fragments produce the same findings regardless of fragment order.
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"version": 1,
|
|
12
|
+
"fingerprintVersion": "awi-sha256-v1",
|
|
13
|
+
"complete": true,
|
|
14
|
+
"summaries": [
|
|
15
|
+
{ "traceId": "demo", "operations": 0, "toolCalls": 0, "maxDepth": 0 }
|
|
16
|
+
],
|
|
17
|
+
"findings": [],
|
|
18
|
+
"warnings": []
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
complete:false means truncation and is never clean success. Eligibility warnings identify unavailable timing/argument evidence. Complete means all applicable analysis finished, not that absent information was reconstructed. Incompatible contracts require a version change. Evidence fields are rule-specific and additive; consumers should not parse prose.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"version": { "default": 1, "type": "number", "const": 1 },
|
|
6
|
+
"rules": {
|
|
7
|
+
"default": {
|
|
8
|
+
"AWI001": { "enabled": true, "threshold": 4 },
|
|
9
|
+
"AWI002": { "enabled": true, "threshold": 3 },
|
|
10
|
+
"AWI003": { "enabled": true, "minRepetitions": 3, "maxCycleLength": 6 },
|
|
11
|
+
"AWI004": { "enabled": true, "threshold": 5, "windowSeconds": 30 },
|
|
12
|
+
"AWI005": { "enabled": true, "maxOperations": 100, "maxDepth": 20 },
|
|
13
|
+
"AWI006": { "enabled": true }
|
|
14
|
+
},
|
|
15
|
+
"type": "object",
|
|
16
|
+
"properties": {
|
|
17
|
+
"AWI001": {
|
|
18
|
+
"default": { "enabled": true, "threshold": 4 },
|
|
19
|
+
"type": "object",
|
|
20
|
+
"properties": {
|
|
21
|
+
"enabled": { "default": true, "type": "boolean" },
|
|
22
|
+
"threshold": {
|
|
23
|
+
"default": 4,
|
|
24
|
+
"type": "integer",
|
|
25
|
+
"minimum": 2,
|
|
26
|
+
"maximum": 100
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"required": ["enabled", "threshold"],
|
|
30
|
+
"additionalProperties": false
|
|
31
|
+
},
|
|
32
|
+
"AWI002": {
|
|
33
|
+
"default": { "enabled": true, "threshold": 3 },
|
|
34
|
+
"type": "object",
|
|
35
|
+
"properties": {
|
|
36
|
+
"enabled": { "default": true, "type": "boolean" },
|
|
37
|
+
"threshold": {
|
|
38
|
+
"default": 3,
|
|
39
|
+
"type": "integer",
|
|
40
|
+
"minimum": 2,
|
|
41
|
+
"maximum": 100
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"required": ["enabled", "threshold"],
|
|
45
|
+
"additionalProperties": false
|
|
46
|
+
},
|
|
47
|
+
"AWI003": {
|
|
48
|
+
"default": {
|
|
49
|
+
"enabled": true,
|
|
50
|
+
"minRepetitions": 3,
|
|
51
|
+
"maxCycleLength": 6
|
|
52
|
+
},
|
|
53
|
+
"type": "object",
|
|
54
|
+
"properties": {
|
|
55
|
+
"enabled": { "default": true, "type": "boolean" },
|
|
56
|
+
"minRepetitions": {
|
|
57
|
+
"default": 3,
|
|
58
|
+
"type": "integer",
|
|
59
|
+
"minimum": 2,
|
|
60
|
+
"maximum": 100
|
|
61
|
+
},
|
|
62
|
+
"maxCycleLength": {
|
|
63
|
+
"default": 6,
|
|
64
|
+
"type": "integer",
|
|
65
|
+
"minimum": 2,
|
|
66
|
+
"maximum": 16
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"required": ["enabled", "minRepetitions", "maxCycleLength"],
|
|
70
|
+
"additionalProperties": false
|
|
71
|
+
},
|
|
72
|
+
"AWI004": {
|
|
73
|
+
"default": { "enabled": true, "threshold": 5, "windowSeconds": 30 },
|
|
74
|
+
"type": "object",
|
|
75
|
+
"properties": {
|
|
76
|
+
"enabled": { "default": true, "type": "boolean" },
|
|
77
|
+
"threshold": {
|
|
78
|
+
"default": 5,
|
|
79
|
+
"type": "integer",
|
|
80
|
+
"minimum": 2,
|
|
81
|
+
"maximum": 100
|
|
82
|
+
},
|
|
83
|
+
"windowSeconds": {
|
|
84
|
+
"default": 30,
|
|
85
|
+
"type": "integer",
|
|
86
|
+
"minimum": 1,
|
|
87
|
+
"maximum": 3600
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
"required": ["enabled", "threshold", "windowSeconds"],
|
|
91
|
+
"additionalProperties": false
|
|
92
|
+
},
|
|
93
|
+
"AWI005": {
|
|
94
|
+
"default": { "enabled": true, "maxOperations": 100, "maxDepth": 20 },
|
|
95
|
+
"type": "object",
|
|
96
|
+
"properties": {
|
|
97
|
+
"enabled": { "default": true, "type": "boolean" },
|
|
98
|
+
"maxOperations": {
|
|
99
|
+
"default": 100,
|
|
100
|
+
"type": "integer",
|
|
101
|
+
"minimum": 1,
|
|
102
|
+
"maximum": 50000
|
|
103
|
+
},
|
|
104
|
+
"maxDepth": {
|
|
105
|
+
"default": 20,
|
|
106
|
+
"type": "integer",
|
|
107
|
+
"minimum": 1,
|
|
108
|
+
"maximum": 1000
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"required": ["enabled", "maxOperations", "maxDepth"],
|
|
112
|
+
"additionalProperties": false
|
|
113
|
+
},
|
|
114
|
+
"AWI006": {
|
|
115
|
+
"default": { "enabled": true },
|
|
116
|
+
"type": "object",
|
|
117
|
+
"properties": { "enabled": { "default": true, "type": "boolean" } },
|
|
118
|
+
"required": ["enabled"],
|
|
119
|
+
"additionalProperties": false
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
"required": ["AWI001", "AWI002", "AWI003", "AWI004", "AWI005", "AWI006"],
|
|
123
|
+
"additionalProperties": false
|
|
124
|
+
},
|
|
125
|
+
"fingerprint": {
|
|
126
|
+
"default": { "ignorePaths": [] },
|
|
127
|
+
"type": "object",
|
|
128
|
+
"properties": {
|
|
129
|
+
"ignorePaths": {
|
|
130
|
+
"default": [],
|
|
131
|
+
"maxItems": 100,
|
|
132
|
+
"type": "array",
|
|
133
|
+
"items": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"pattern": "^[A-Za-z_][A-Za-z0-9_]*(\\.[A-Za-z_][A-Za-z0-9_]*)*$"
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"required": ["ignorePaths"],
|
|
140
|
+
"additionalProperties": false
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"required": ["version", "rules", "fingerprint"],
|
|
144
|
+
"additionalProperties": false
|
|
145
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"version": { "type": "number", "const": 1 },
|
|
6
|
+
"fingerprintVersion": { "type": "string", "const": "awi-sha256-v1" },
|
|
7
|
+
"complete": { "type": "boolean" },
|
|
8
|
+
"summaries": {
|
|
9
|
+
"type": "array",
|
|
10
|
+
"items": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"traceId": { "type": "string" },
|
|
14
|
+
"operations": {
|
|
15
|
+
"type": "integer",
|
|
16
|
+
"minimum": 0,
|
|
17
|
+
"maximum": 9007199254740991
|
|
18
|
+
},
|
|
19
|
+
"toolCalls": {
|
|
20
|
+
"type": "integer",
|
|
21
|
+
"minimum": 0,
|
|
22
|
+
"maximum": 9007199254740991
|
|
23
|
+
},
|
|
24
|
+
"maxDepth": {
|
|
25
|
+
"type": "integer",
|
|
26
|
+
"minimum": 0,
|
|
27
|
+
"maximum": 9007199254740991
|
|
28
|
+
},
|
|
29
|
+
"durationMs": { "type": "number", "minimum": 0 },
|
|
30
|
+
"usage": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"properties": {
|
|
33
|
+
"inputTokens": {
|
|
34
|
+
"type": "integer",
|
|
35
|
+
"minimum": 0,
|
|
36
|
+
"maximum": 9007199254740991
|
|
37
|
+
},
|
|
38
|
+
"outputTokens": {
|
|
39
|
+
"type": "integer",
|
|
40
|
+
"minimum": 0,
|
|
41
|
+
"maximum": 9007199254740991
|
|
42
|
+
},
|
|
43
|
+
"totalTokens": {
|
|
44
|
+
"type": "integer",
|
|
45
|
+
"minimum": 0,
|
|
46
|
+
"maximum": 9007199254740991
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"additionalProperties": false
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"required": ["traceId", "operations", "toolCalls", "maxDepth"],
|
|
53
|
+
"additionalProperties": false
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"findings": {
|
|
57
|
+
"type": "array",
|
|
58
|
+
"items": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"properties": {
|
|
61
|
+
"ruleId": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"enum": ["AWI001", "AWI002", "AWI003", "AWI004", "AWI005", "AWI006"]
|
|
64
|
+
},
|
|
65
|
+
"severity": { "type": "string", "enum": ["low", "medium", "high"] },
|
|
66
|
+
"confidence": { "type": "string", "enum": ["high", "moderate"] },
|
|
67
|
+
"title": { "type": "string" },
|
|
68
|
+
"description": { "type": "string" },
|
|
69
|
+
"explanation": { "type": "string" },
|
|
70
|
+
"remediation": { "type": "string" },
|
|
71
|
+
"traceId": { "type": "string" },
|
|
72
|
+
"operationIds": { "type": "array", "items": { "type": "string" } },
|
|
73
|
+
"evidence": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"propertyNames": { "type": "string" },
|
|
76
|
+
"additionalProperties": {}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"required": [
|
|
80
|
+
"ruleId",
|
|
81
|
+
"severity",
|
|
82
|
+
"confidence",
|
|
83
|
+
"title",
|
|
84
|
+
"description",
|
|
85
|
+
"explanation",
|
|
86
|
+
"remediation",
|
|
87
|
+
"traceId",
|
|
88
|
+
"operationIds",
|
|
89
|
+
"evidence"
|
|
90
|
+
],
|
|
91
|
+
"additionalProperties": false
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"warnings": { "type": "array", "items": { "type": "string" } },
|
|
95
|
+
"content": {
|
|
96
|
+
"type": "array",
|
|
97
|
+
"items": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"properties": {
|
|
100
|
+
"traceId": { "type": "string" },
|
|
101
|
+
"operations": { "type": "array", "items": {} }
|
|
102
|
+
},
|
|
103
|
+
"required": ["traceId", "operations"],
|
|
104
|
+
"additionalProperties": false
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"required": [
|
|
109
|
+
"version",
|
|
110
|
+
"fingerprintVersion",
|
|
111
|
+
"complete",
|
|
112
|
+
"summaries",
|
|
113
|
+
"findings",
|
|
114
|
+
"warnings"
|
|
115
|
+
],
|
|
116
|
+
"additionalProperties": false
|
|
117
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"version": { "type": "number", "const": 1 },
|
|
6
|
+
"traceId": { "type": "string", "minLength": 1 },
|
|
7
|
+
"metadata": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"propertyNames": { "type": "string" },
|
|
10
|
+
"additionalProperties": {}
|
|
11
|
+
},
|
|
12
|
+
"operations": {
|
|
13
|
+
"maxItems": 50000,
|
|
14
|
+
"type": "array",
|
|
15
|
+
"items": {
|
|
16
|
+
"type": "object",
|
|
17
|
+
"properties": {
|
|
18
|
+
"id": { "type": "string", "minLength": 1 },
|
|
19
|
+
"parentId": { "type": "string", "minLength": 1 },
|
|
20
|
+
"type": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"enum": ["tool", "agent", "model", "workflow", "generic"]
|
|
23
|
+
},
|
|
24
|
+
"name": { "type": "string", "minLength": 1 },
|
|
25
|
+
"toolName": { "type": "string" },
|
|
26
|
+
"startTime": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"format": "date-time",
|
|
29
|
+
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
|
|
30
|
+
},
|
|
31
|
+
"endTime": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"format": "date-time",
|
|
34
|
+
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
|
|
35
|
+
},
|
|
36
|
+
"status": {
|
|
37
|
+
"default": "unknown",
|
|
38
|
+
"type": "string",
|
|
39
|
+
"enum": ["ok", "error", "unknown"]
|
|
40
|
+
},
|
|
41
|
+
"error": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"properties": {
|
|
44
|
+
"code": { "type": "string" },
|
|
45
|
+
"message": { "type": "string" }
|
|
46
|
+
},
|
|
47
|
+
"additionalProperties": false
|
|
48
|
+
},
|
|
49
|
+
"arguments": {},
|
|
50
|
+
"resultMetadata": {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"propertyNames": { "type": "string" },
|
|
53
|
+
"additionalProperties": {}
|
|
54
|
+
},
|
|
55
|
+
"usage": {
|
|
56
|
+
"type": "object",
|
|
57
|
+
"properties": {
|
|
58
|
+
"inputTokens": {
|
|
59
|
+
"type": "integer",
|
|
60
|
+
"minimum": 0,
|
|
61
|
+
"maximum": 9007199254740991
|
|
62
|
+
},
|
|
63
|
+
"outputTokens": {
|
|
64
|
+
"type": "integer",
|
|
65
|
+
"minimum": 0,
|
|
66
|
+
"maximum": 9007199254740991
|
|
67
|
+
},
|
|
68
|
+
"totalTokens": {
|
|
69
|
+
"type": "integer",
|
|
70
|
+
"minimum": 0,
|
|
71
|
+
"maximum": 9007199254740991
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"additionalProperties": false
|
|
75
|
+
},
|
|
76
|
+
"cost": {
|
|
77
|
+
"type": "object",
|
|
78
|
+
"properties": {
|
|
79
|
+
"amount": { "type": "number", "minimum": 0 },
|
|
80
|
+
"currency": { "type": "string", "pattern": "^[A-Z]{3}$" }
|
|
81
|
+
},
|
|
82
|
+
"required": ["amount", "currency"],
|
|
83
|
+
"additionalProperties": false
|
|
84
|
+
},
|
|
85
|
+
"metadata": {
|
|
86
|
+
"type": "object",
|
|
87
|
+
"propertyNames": { "type": "string" },
|
|
88
|
+
"additionalProperties": {}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
"required": ["id", "type", "name", "status"],
|
|
92
|
+
"additionalProperties": false
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
"required": ["version", "traceId", "operations"],
|
|
97
|
+
"additionalProperties": false
|
|
98
|
+
}
|
package/docs/security.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Security and privacy
|
|
2
|
+
|
|
3
|
+
All trace content is inert untrusted JSON. AWI never obeys embedded instructions. Analysis uses no eval, shell execution, URL fetching, telemetry, network, database or intermediate content files. Fixed limits apply to parsing, files, spans, nesting, strings, traversal, cycles, findings and default report amplification. Shared native values count once per occurrence against the processed-content budget.
|
|
4
|
+
|
|
5
|
+
Ordinary reports include trace/operation IDs, measurements, rule IDs, source indices and explanations. Raw tool names, names, arguments, results, prompts, metadata, error messages and hashes are omitted. IDs can themselves contain secrets; export opaque safe IDs. JSON output escapes terminal formatting and control characters while preserving identifiers on JSON parsing. Human output replaces Unicode controls, formatting controls and line/paragraph separators including ANSI escape and bidirectional controls. Human output colors severity only on supported interactive terminals, honors NO_COLOR and --no-color, and never relies solely on color.
|
|
6
|
+
|
|
7
|
+
--show-content includes content and can expose private data. Recursive redaction replaces values by keys matching authorization, password, secret, token, API key, cookie, credential, prompt, message, output or result. It cannot recognize every secret, especially arbitrary argument strings and IDs. Review locally before sharing. AWI never transmits content. Default reports also contain sensitive workflow correlations.
|
|
8
|
+
|
|
9
|
+
Parsing rejects `__proto__`, `constructor` and `prototype` object keys. No prototype merging occurs. Files are size-checked before parsing, decoded as strict UTF-8 (optional BOM accepted), and read in bounded chunks even if they grow. Aggregate limits count bytes actually read. CLI rejects symlink entries and symlink components of trace/configuration/output paths. Resolve legitimate OS aliases (including macOS /var temporary paths) to real paths first. POSIX opens also use O_NOFOLLOW, and all platforms compare the opened file identity with the checked file. This is not a filesystem sandbox against concurrent replacement of directory components: use trusted directories and OS permissions. Reports use an adjacent temporary file with mode 0600 on POSIX (Windows uses inherited filesystem ACLs), fsync and atomic exclusive hard-link publication. Existing targets are never overwritten. Cleanup is attempted on ordinary failures; OS errors can prevent removal. Filesystems without hard links fail closed. Crashes or cleanup failures may leave .tmp files to inspect/remove locally under the same filesystem permissions.
|
|
10
|
+
|
|
11
|
+
Broken stdout pipes produce a controlled operational failure, without an uncaught stack trace. Errors omit raw parser content, OS filenames and stacks. Schema errors show field paths/codes. No general anonymization guarantee exists for identifiers or opt-in content. See SECURITY.md for private disclosure guidance.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Trace formats
|
|
2
|
+
|
|
3
|
+
## AWI JSON v1
|
|
4
|
+
|
|
5
|
+
A trace is `{version:1, traceId:string, metadata?:object, operations:Operation[]}`. A collection is `{version:1, traces:Trace[]}`. Unknown fields fail validation; use metadata for extensions. See the [single-trace schema](schemas/trace.schema.json); ingest validates the collection wrapper separately.
|
|
6
|
+
|
|
7
|
+
Required operation fields: id, type, name. Types: tool, agent, model, workflow, generic. Status: ok, error, unknown (default). Optional fields: parentId, toolName, startTime, endTime, error:{code?,message?}, arguments (any JSON), resultMetadata, usage:{inputTokens?,outputTokens?,totalTokens?}, cost:{amount,currency}, metadata.
|
|
8
|
+
|
|
9
|
+
Times are offset-bearing ISO-8601 strings, optional independently, with at most nine fractional-second digits. Ordering and overlap use exact nanosecond comparisons; summary duration is a floating-point millisecond measurement. Duration is derived only with both endpoints; end-before-start fails. Trace duration is reported only if all operations have both endpoints. Token counts are nonnegative safe integers; cost is a finite nonnegative amount with a three-letter uppercase currency. Missing resources stay absent; recorded zero stays zero. IDs are nonempty and unique per trace. Missing parents warn and yield lower-bound depth; cycles and duplicates fail. Root depth is 1, empty trace depth 0. Generic operations remain in structure/counts.
|
|
10
|
+
|
|
11
|
+
## OTLP JSON subset
|
|
12
|
+
|
|
13
|
+
Accepts protobuf JSON-shaped resourceSpans[].scopeSpans[].spans[]. Does not accept JSONL, legacy instrumentationLibrarySpans, binary protobuf, collector envelopes or proprietary framework exports. Trace IDs are nonzero 32-digit hex; span/parent IDs are 16-digit hex, lowercased. Empty/all-zero parent means root. Missing trace/span IDs fail. Missing scopeSpans/spans are empty; malformed arrays fail.
|
|
14
|
+
|
|
15
|
+
| Input | Canonical mapping |
|
|
16
|
+
| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| traceId, spanId, parentSpanId, name | identity, parent, name |
|
|
18
|
+
| startTimeUnixNano / endTimeUnixNano | ISO times retaining nanosecond precision; decimal strings; numbers must be safe integers |
|
|
19
|
+
| status.code 1 / STATUS_CODE_OK | ok |
|
|
20
|
+
| status.code 2 / STATUS_CODE_ERROR | error |
|
|
21
|
+
| absent, 0, unrecognized status | unknown |
|
|
22
|
+
| gen_ai.tool.name or tool.name | toolName and tool classification |
|
|
23
|
+
| gen_ai.operation.name = execute_tool | tool |
|
|
24
|
+
| invoke_agent | agent |
|
|
25
|
+
| chat, generate_content, text_completion, embeddings | model |
|
|
26
|
+
| workflow | workflow |
|
|
27
|
+
| unrecognized operation | generic |
|
|
28
|
+
| gen_ai.tool.call.arguments | arguments; JSON strings parsed if valid, otherwise string preserved |
|
|
29
|
+
| gen_ai.usage.input_tokens / output_tokens / total_tokens | separately recorded nonnegative safe integer usage |
|
|
30
|
+
| exception event: exception.type / exception.message; error status.message | error code/message; status message is a fallback; exception alone does not promote unknown to failure |
|
|
31
|
+
| resource attributes, scope, attributes, events | preserved metadata |
|
|
32
|
+
|
|
33
|
+
Tool name takes precedence over operation classification. No span-name guessing. No results, costs, or usage are invented. AnyValue supports stringValue, boolValue, intValue, doubleValue, bytesValue, arrayValue.values, kvlistValue.values. Integer AnyValues must be signed 64-bit decimal strings or safe integer JSON numbers. Large values outside the safe numeric range stay strings and cannot become usage. Malformed known AnyValue encodings fail; empty or unknown encodings are omitted rather than turned into tool arguments. Arrays/objects with unknown members are not used as argument identity evidence. No type coercion fabricates usage.
|
|
34
|
+
|
|
35
|
+
These are limited GenAI semantic-convention assumptions, not universal compatibility. Instrumentation can use different attributes or omit arguments; inspect warnings or explicitly convert to AWI JSON. No framework integration has been certified. Positive-duration nanosecond spans retain their precision; truly zero-duration groups are excluded conservatively. Attributes/events remain in memory and are absent from default reports.
|
|
36
|
+
|
|
37
|
+
Auto-detection requires exactly one recognizable AWI/OTLP root. Otherwise specify input-format or fix the export. Malformed required data fails as a unit rather than silently dropping spans.
|
|
38
|
+
|
|
39
|
+
Resource attributes are decoded once per resourceSpans entry. Raw and normalized structures each have a bounded node budget; exports with extreme shared-metadata amplification fail explicitly. Binary AnyValues remain opaque base64 metadata and are not interpreted as tool/operation names, errors or argument identity. Unknown duplicate attributes clear earlier evidence rather than reviving stale values. Timestamp zero means missing; negative/out-of-uint64 timestamps fail. Unknown attributes remain outside classification; this subset does not certify every semantic-convention revision.
|
|
40
|
+
|
|
41
|
+
For missing or empty span names, the display label is unnamed and metadata awi.operation_name_recorded is false. An explicit execute_tool span still counts as a tool, but identity rules exclude it unless an actual tool name or recorded operation name is available. The display placeholder never establishes tool identity.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
- Invalid JSON/schema: use UTF-8 JSON, supported version and fields. JSONL/collector envelopes need conversion.
|
|
4
|
+
- Ambiguous format: remove conflicting roots or explicitly select input-format; schemas still apply.
|
|
5
|
+
- No sequence findings: read warnings. Recorded arguments, positive-duration times, shared parent and non-overlap are required. Patterns may span bounded windows.
|
|
6
|
+
- Duplicate IDs: files sharing traceId merge. Give unrelated executions different identities.
|
|
7
|
+
- Filesystem failure: check permissions, existing output, regular inputs and hard-link support. Use a new report path.
|
|
8
|
+
- Directory rejected: use a dedicated JSON trace directory; empty dirs, symlinks, unrelated files and excessive recursion fail.
|
|
9
|
+
- Exit 2: severity met fail-on (default high). Review evidence or adjust policy.
|
|
10
|
+
- Exit 1/incomplete: fix input or split workloads. Finding overflow is not success.
|
|
11
|
+
- Missing tokens/cost: unknown remains absent; parent totals are not attributed to tools.
|
|
12
|
+
- Install engines failure: use supported Node. Do not bypass engines or delete lockfile to hide incompatibility.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Verification record
|
|
2
|
+
|
|
3
|
+
Audit date: October 19, 2026. Package manager: pnpm 11.25.0. Baseline implementation: 9bc61c1. See the historical [audit findings](audit.md) and current [release procedure](releasing.md).
|
|
4
|
+
|
|
5
|
+
## Hosted CI and release candidate
|
|
6
|
+
|
|
7
|
+
The [nine-job hosted CI run](https://github.com/jigar48/awi/actions/runs/39208844432) completed successfully for `d2514af3e9d4a7b11a53308a77ab2ca40333d8ce`. Windows, macOS and Ubuntu each passed on Node 22.18.0, 24.11.0 and 26, including all 69 tests, typecheck/lint/build, formatting, schema consistency, production dependency audit, packing and isolated package installation/API checks.
|
|
8
|
+
|
|
9
|
+
The platform table below records the earlier local audit only. The hosted run resolves that audit's platform gate for d2514af. Later local release-preparation changes affect documentation/package metadata, not runtime source or dependencies. They require a fresh nine-job main run before publication; earlier CI does not validate them. No tag, GitHub release or npm publication has been performed.
|
|
10
|
+
|
|
11
|
+
Release-preparation validation on Windows Node 24.19.0: `pnpm install --frozen-lockfile`, `pnpm check` (69 passing tests, typecheck, lint, JS/declaration build), `pnpm format:check`, `pnpm schemas` (before/after schema hashes unchanged), full `pnpm audit` (no known vulnerabilities), `pnpm smoke:package` (clean external consumer, all shipped examples/options, ESM and strict TypeScript imports), and `git diff --check` passed. Runtime source, tests, dependencies, lockfile and CI workflow remain identical to d2514af. Local documentation links and prepared GitHub topic syntax were checked; version-tagged release links intentionally await tag creation. Maintainer GitHub/portfolio/support links responded successfully; LinkedIn rejects HEAD checks, so that supplied URL is retained without claiming live content verification. Candidate artifact byte counts, SHA-256/SHA-512 and source SHA are recorded outside the package in the local release manifest.
|
|
12
|
+
|
|
13
|
+
## Actual platform checks
|
|
14
|
+
|
|
15
|
+
| Environment | Node | Full tests | Typecheck / lint / full build | Isolated installed-package check |
|
|
16
|
+
| ----------------------------- | ------------------------- | ---------- | ----------------------------- | -------------------------------- |
|
|
17
|
+
| Windows x64 | 24.19.0 | 69 passed | Passed | Passed |
|
|
18
|
+
| Ubuntu 24.04.3 x64 under WSL2 | 24.19.0 | 69 passed | Passed | Passed |
|
|
19
|
+
| Ubuntu 24.04.3 x64 under WSL2 | 22.18.0 | 69 passed | Passed | Passed |
|
|
20
|
+
| Ubuntu 24.04.3 x64 under WSL2 | 26.0.0 | 69 passed | Passed | Passed |
|
|
21
|
+
| macOS | Not available | Not run | Not run | Not run |
|
|
22
|
+
| Hosted GitHub Actions matrix | 9 configured combinations | Not run | Not run | Not run |
|
|
23
|
+
|
|
24
|
+
WSL uses real Linux Node distributions and independent Linux dependencies. Downloaded runtimes were verified against their official SHA-256 manifests. WSL/NTFS and Linux temporary-filesystem checks do not certify macOS or hosted runners. Windows Node 22/26 and the exact Node 24.11.0 floor remain for CI to verify. Future Node majors are not verified by these runs.
|
|
25
|
+
|
|
26
|
+
## Checks performed
|
|
27
|
+
|
|
28
|
+
- Strict TypeScript, ESLint and full JS/declaration builds.
|
|
29
|
+
- 69 tests: original 37 plus 32 targeted audit regressions, with additional assertions in existing tests. All six rules have positive/negative coverage; tests include exact concurrency/time boundaries, failure interactions, graph errors, OTLP encodings, source validation, deterministic fragments and capped findings, hostile content, terminal/JSON escaping, UTF-8/BOM, symlinks, overwrite/hardlink/long-name handling, resource budgets and operational exit codes.
|
|
30
|
+
- The original 50,000-operation performance assertion remains unchanged. Specific hostile metadata, wide-array and shared-string vectors return controlled errors in a 96 MiB child heap; this is not a universal minimum-memory guarantee for all maximum-size inputs.
|
|
31
|
+
- Network, DNS/fetch and subprocess APIs were blocked while analyzing hostile trace values; no external effects occurred.
|
|
32
|
+
- Frozen-lockfile installation, formatting, whitespace checks and deterministic schema regeneration.
|
|
33
|
+
- Full pnpm audit reported no known dependency vulnerabilities at audit time. This result can change; it does not assess all Node/OS vulnerabilities or prove absence of defects.
|
|
34
|
+
- Actual tarball allowlist, executable mode/shebang, bin/ESM/type export mappings, included maintainer documents, installation outside the repository with scripts disabled, all shipped examples/options and strict consumer TypeScript imports.
|
|
35
|
+
|
|
36
|
+
A cold-filesystem declaration compilation timeout and a multi-process CLI test timeout were diagnosed as test-harness I/O budgets, not waived product assertions. CLI setup compiles runtime JS with a bounded setup timeout; full builds/consumer typechecks still verify declarations. CLI subprocesses and end-to-end I/O cases have explicit budgets; detector performance checks remain unchanged.
|
|
37
|
+
|
|
38
|
+
During the original local audit, no remote mutations occurred. The later authorized feature-branch push produced the successful hosted run above. **Publication remains blocked until approval, a fresh successful matrix on the final main candidate, and npm authentication/name checks.** See [release procedure](releasing.md).
|