aienvmp 0.1.43 → 0.1.45
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/BUGFIXES.md +12 -0
- package/CHANGELOG.md +20 -0
- package/README.md +3 -4
- package/package.json +1 -1
- package/src/actions.js +4 -0
- package/src/cli.js +6 -1
- package/src/commands/checkpoint.js +68 -0
- package/src/commands/handoff.js +3 -1
- package/src/commands/plan.js +10 -6
- package/src/commands/record.js +3 -1
- package/src/commands/status.js +1 -0
- package/src/contract.js +1 -1
- package/src/decision.js +1 -0
- package/src/doctor.js +44 -0
- package/src/enforcement.js +1 -1
- package/src/manifest.js +2 -1
- package/src/preflight.js +77 -1
- package/src/render.js +28 -12
package/BUGFIXES.md
CHANGED
|
@@ -46,6 +46,18 @@ Short record of bugs, fixes, and follow-up checks.
|
|
|
46
46
|
- Fix: `record` timeline entries now include follow-up metadata, and status/context/dashboard surface unresolved follow-ups.
|
|
47
47
|
- Verification: Windows and macOS candidate smoke checks confirmed `record --target dependency` appears in `status --json` followUps and the dashboard Follow-ups card.
|
|
48
48
|
|
|
49
|
+
### Multi-agent records were less visible than open intents
|
|
50
|
+
|
|
51
|
+
- Issue: open intents showed planning conflicts, but two agents could record changes to the same env target after a handoff without a compact coordination signal.
|
|
52
|
+
- Fix: `doctor`, status/context, handoff, and the dashboard now expose same-target multi-agent records through advisory warnings and `agentActivity`.
|
|
53
|
+
- Verification: tests cover multi-agent record detection, later handoff reset, status JSON, handoff text, dashboard HTML, and recommended actions.
|
|
54
|
+
|
|
55
|
+
### Post-change loop was too many commands for routine AI handoff
|
|
56
|
+
|
|
57
|
+
- Issue: after an environment change, agents had to remember separate record, sync, status, and handoff commands.
|
|
58
|
+
- Fix: `aienvmp checkpoint` now performs the post-change loop and records an explicit sync ledger entry so follow-ups can be closed.
|
|
59
|
+
- Verification: checkpoint tests cover text/JSON output, status artifact refresh, timeline entries, handoff recording, and cleared follow-ups.
|
|
60
|
+
|
|
49
61
|
## Template
|
|
50
62
|
|
|
51
63
|
### Title
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.45
|
|
4
|
+
|
|
5
|
+
- Added `aienvmp checkpoint` as a one-command post-environment-change flow for record, sync, status, and handoff.
|
|
6
|
+
- Added quiet command support so checkpoint can compose existing commands without noisy intermediate output.
|
|
7
|
+
- Added checkpoint sync ledger entries so follow-up detection can prove the refresh step happened.
|
|
8
|
+
- Added checkpoint guidance to the shared preflight, decision, manifest, dependency protocol, and status outputs.
|
|
9
|
+
- Updated AIENV, handoff, plan, dashboard, and agent pointer rendering to prefer checkpoint after environment changes.
|
|
10
|
+
- Shortened the README environment-change flow to intent plus checkpoint.
|
|
11
|
+
- Added checkpoint regression coverage for JSON output, ledger updates, follow-up closure, status artifacts, and rendered guidance.
|
|
12
|
+
|
|
13
|
+
## 0.1.44
|
|
14
|
+
|
|
15
|
+
- Added multi-agent record warnings when multiple agents record environment changes for the same target after the last handoff.
|
|
16
|
+
- Added `agentActivity` to the shared preflight contract so status/context/handoff can expose recent env records by target and actor.
|
|
17
|
+
- Surfaced agent activity in context, handoff, and the dashboard for faster shared-server review.
|
|
18
|
+
- Added a recommended handoff action for multi-agent record activity.
|
|
19
|
+
- Mapped `multi-agent-records` into the optional `doctor --strict coordination` scope while keeping local behavior advisory by default.
|
|
20
|
+
- Documented the new AI contract field in the compact README.
|
|
21
|
+
- Added regression tests for warning detection, handoff reset behavior, status JSON, dashboard HTML, and recommended actions.
|
|
22
|
+
|
|
3
23
|
## 0.1.43
|
|
4
24
|
|
|
5
25
|
- Added follow-up metadata to `record` timeline entries so dependency/security changes point agents back to sync, status, and handoff.
|
package/README.md
CHANGED
|
@@ -23,10 +23,7 @@ Before environment changes:
|
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
npx aienvmp intent --actor agent:codex --action "change dependency" --target dependency
|
|
26
|
-
npx aienvmp
|
|
27
|
-
npx aienvmp sync
|
|
28
|
-
npx aienvmp status --write
|
|
29
|
-
npx aienvmp handoff --record --actor agent:codex
|
|
26
|
+
npx aienvmp checkpoint --actor agent:codex --summary "dependency-change" --target dependency
|
|
30
27
|
```
|
|
31
28
|
|
|
32
29
|
Use `--dir <workspace>` when AI or CI runs outside the target project.
|
|
@@ -51,6 +48,7 @@ AIENV.md # Markdown env map for AI agents
|
|
|
51
48
|
- `status.json.nextAgent` tells the next AI what to read and whether to review first.
|
|
52
49
|
- `dependencyReadSet` lists manifests and lockfiles before package or security changes.
|
|
53
50
|
- `coordination.conflictTargets` shows where multiple agents are planning changes.
|
|
51
|
+
- `agentActivity.multiActorTargets` shows where multiple agents actually recorded env changes.
|
|
54
52
|
- `followUps` shows records that still need `sync`, `status`, or `handoff`.
|
|
55
53
|
- `handoff` carries dependency read-set and protocol guidance for the next AI.
|
|
56
54
|
- Light SBOM includes source/confidence hints; verify security claims with dedicated scanners.
|
|
@@ -78,6 +76,7 @@ aienvmp plan --write # read-only action plan
|
|
|
78
76
|
aienvmp handoff --record # next-agent summary
|
|
79
77
|
aienvmp intent # record planned env change
|
|
80
78
|
aienvmp record # record what changed
|
|
79
|
+
aienvmp checkpoint # record + sync + status + handoff after env change
|
|
81
80
|
aienvmp doctor --strict security|policy|coordination|all
|
|
82
81
|
```
|
|
83
82
|
|
package/package.json
CHANGED
package/src/actions.js
CHANGED
|
@@ -15,6 +15,10 @@ export function recommendedActions(manifest = {}, context = {}) {
|
|
|
15
15
|
actions.push(action("coordinate-agents", "high", "coordination", "Multiple agents are planning changes to the same environment target. Coordinate with the user before proceeding."));
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
if (warnings.some((warning) => warning.code === "multi-agent-records")) {
|
|
19
|
+
actions.push(action("review-agent-activity", "high", "coordination", "Multiple agents recorded changes for the same environment target. Review activity and record a handoff before more environment work.", "aienvmp handoff --record --actor agent:id"));
|
|
20
|
+
}
|
|
21
|
+
|
|
18
22
|
actions.push(...securityActions(manifest.security));
|
|
19
23
|
|
|
20
24
|
if (hasRuntimePolicyWarning(warnings)) {
|
package/src/cli.js
CHANGED
|
@@ -14,6 +14,7 @@ import { handoffWorkspace } from "./commands/handoff.js";
|
|
|
14
14
|
import { planWorkspace } from "./commands/plan.js";
|
|
15
15
|
import { statusWorkspace } from "./commands/status.js";
|
|
16
16
|
import { schemaWorkspace } from "./commands/schema.js";
|
|
17
|
+
import { checkpointWorkspace } from "./commands/checkpoint.js";
|
|
17
18
|
import { readFileSync } from "node:fs";
|
|
18
19
|
|
|
19
20
|
const commands = new Map([
|
|
@@ -32,7 +33,8 @@ const commands = new Map([
|
|
|
32
33
|
["handoff", handoffWorkspace],
|
|
33
34
|
["plan", planWorkspace],
|
|
34
35
|
["status", statusWorkspace],
|
|
35
|
-
["schema", schemaWorkspace]
|
|
36
|
+
["schema", schemaWorkspace],
|
|
37
|
+
["checkpoint", checkpointWorkspace]
|
|
36
38
|
]);
|
|
37
39
|
|
|
38
40
|
const version = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
@@ -108,6 +110,7 @@ Usage:
|
|
|
108
110
|
aienvmp context [--dir .] [--json]
|
|
109
111
|
aienvmp status [--dir .] [--json] [--write] [--quiet]
|
|
110
112
|
aienvmp handoff [--dir .] [--json] [--record --actor agent:id]
|
|
113
|
+
aienvmp checkpoint [--dir .] --actor agent:id --summary "what changed" [--target dependency] [--json]
|
|
111
114
|
aienvmp plan [--dir .] [--json] [--write]
|
|
112
115
|
aienvmp schema [--json]
|
|
113
116
|
|
|
@@ -116,6 +119,7 @@ Common:
|
|
|
116
119
|
aienvmp status print one simple environment decision; --write saves .aienvmp/status.json
|
|
117
120
|
aienvmp context print the AI preflight brief
|
|
118
121
|
aienvmp handoff print the next-agent handoff summary
|
|
122
|
+
aienvmp checkpoint record, sync, status, and handoff after an env change
|
|
119
123
|
aienvmp plan print a read-only AI environment action plan
|
|
120
124
|
aienvmp schema print the stable AI-readable output contract
|
|
121
125
|
aienvmp snippet print an AGENTS.md pointer snippet
|
|
@@ -127,6 +131,7 @@ Advanced:
|
|
|
127
131
|
aienvmp intent [--dir .] --actor agent:codex --action "install pnpm"
|
|
128
132
|
aienvmp resolve [--dir .] --actor human:you --id <intent-id> [--status resolved|cancelled]
|
|
129
133
|
aienvmp record [--dir .] --actor agent:codex --summary "updated .nvmrc" [--target node] [--before 20] [--after 24]
|
|
134
|
+
aienvmp checkpoint [--dir .] --actor agent:codex --summary "updated dependency" [--target dependency]
|
|
130
135
|
aienvmp snippet [agents|claude|gemini] [--write AGENTS.md]
|
|
131
136
|
aienvmp compile [--dir .]
|
|
132
137
|
aienvmp diff [--dir .]
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { handoffWorkspace } from "./handoff.js";
|
|
2
|
+
import { recordWorkspace } from "./record.js";
|
|
3
|
+
import { statusWorkspace } from "./status.js";
|
|
4
|
+
import { syncWorkspace } from "./sync.js";
|
|
5
|
+
import { appendJsonLine } from "../fsutil.js";
|
|
6
|
+
import { timelinePath, workspaceDir } from "../paths.js";
|
|
7
|
+
import { observedTrust } from "../trust.js";
|
|
8
|
+
|
|
9
|
+
export async function checkpointWorkspace(args) {
|
|
10
|
+
const actor = required(args.actor, "actor");
|
|
11
|
+
const summary = required(args.summary || args.change, "summary");
|
|
12
|
+
const quiet = args.quiet || args.json;
|
|
13
|
+
const base = { ...args, actor, summary, quiet: true };
|
|
14
|
+
|
|
15
|
+
const record = await recordWorkspace(base);
|
|
16
|
+
const sync = await syncWorkspace({ ...base, json: false, quiet: true });
|
|
17
|
+
await recordCheckpointSync(base);
|
|
18
|
+
const handoff = await handoffWorkspace({ ...base, json: false, record: true, quiet: true });
|
|
19
|
+
const status = await statusWorkspace({ ...base, json: false, write: true, quiet: true });
|
|
20
|
+
|
|
21
|
+
const result = {
|
|
22
|
+
status: "ok",
|
|
23
|
+
mode: "post-environment-change",
|
|
24
|
+
actor,
|
|
25
|
+
summary,
|
|
26
|
+
target: args.target || "",
|
|
27
|
+
record,
|
|
28
|
+
outputs: sync.outputs,
|
|
29
|
+
state: status.state,
|
|
30
|
+
warnings: status.counts?.warnings || 0,
|
|
31
|
+
followUps: status.followUps || [],
|
|
32
|
+
agentActivity: status.agentActivity || {},
|
|
33
|
+
handoff: {
|
|
34
|
+
status: handoff.status,
|
|
35
|
+
recommendedNext: handoff.recommendedNext
|
|
36
|
+
},
|
|
37
|
+
next: "Share .aienvmp/status.json or run `aienvmp context --json` before another environment change."
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
if (args.json) {
|
|
41
|
+
console.log(JSON.stringify(result, null, 2));
|
|
42
|
+
} else if (!quiet) {
|
|
43
|
+
console.log(`checkpoint: ${result.state}`);
|
|
44
|
+
console.log(`record: ${record.summary}${record.target ? ` (${record.target})` : ""}`);
|
|
45
|
+
console.log(`status: ${result.outputs.status}`);
|
|
46
|
+
console.log(`handoff: ${handoff.status}`);
|
|
47
|
+
console.log(`next: ${result.next}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function recordCheckpointSync(args) {
|
|
54
|
+
const now = new Date();
|
|
55
|
+
await appendJsonLine(timelinePath(workspaceDir(args)), {
|
|
56
|
+
at: now.toISOString(),
|
|
57
|
+
actor: args.actor,
|
|
58
|
+
type: "sync",
|
|
59
|
+
summary: "checkpoint sync",
|
|
60
|
+
target: "",
|
|
61
|
+
trust: observedTrust(now)
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function required(value, name) {
|
|
66
|
+
if (!value) throw new Error(`checkpoint: --${name} is required`);
|
|
67
|
+
return String(value);
|
|
68
|
+
}
|
package/src/commands/handoff.js
CHANGED
|
@@ -24,7 +24,7 @@ export async function handoffWorkspace(args) {
|
|
|
24
24
|
|
|
25
25
|
if (args.json) {
|
|
26
26
|
console.log(JSON.stringify(handoff, null, 2));
|
|
27
|
-
} else {
|
|
27
|
+
} else if (!args.quiet) {
|
|
28
28
|
console.log(renderHandoff(handoff));
|
|
29
29
|
}
|
|
30
30
|
return handoff;
|
|
@@ -63,6 +63,7 @@ export function buildHandoff(manifest, timeline = [], warnings = [], intents = [
|
|
|
63
63
|
security: securitySummary(manifest.security),
|
|
64
64
|
dependencyHandoff: dependencyHandoffSummary(preflight),
|
|
65
65
|
coordination: preflight.coordination,
|
|
66
|
+
agentActivity: preflight.agentActivity,
|
|
66
67
|
policy: {
|
|
67
68
|
node: policy.node || "not set",
|
|
68
69
|
python: policy.python || "not set",
|
|
@@ -94,6 +95,7 @@ function dependencyHandoffSummary(preflight = {}) {
|
|
|
94
95
|
packageManagerPolicy: protocol.packageManagerPolicy || "not-detected",
|
|
95
96
|
recordIntent: protocol.commands?.recordIntent || "aienvmp intent --actor agent:id --action planned-change --target dependency",
|
|
96
97
|
recordAfterChange: protocol.commands?.recordAfterChange || "aienvmp record --actor agent:id --summary dependency-change --target dependency",
|
|
98
|
+
checkpointAfterChange: protocol.commands?.checkpointAfterChange || "aienvmp checkpoint --actor agent:id --summary dependency-change --target dependency",
|
|
97
99
|
handoff: protocol.commands?.handoff || "aienvmp handoff --record --actor agent:id"
|
|
98
100
|
}
|
|
99
101
|
};
|
package/src/commands/plan.js
CHANGED
|
@@ -103,7 +103,7 @@ function environmentSteps(warnings = []) {
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
function environmentCategory(code = "") {
|
|
106
|
-
if (["conflicting-open-intents", "stale-open-intent", "handoff-stale"].includes(code)) return "coordination";
|
|
106
|
+
if (["conflicting-open-intents", "stale-open-intent", "handoff-stale", "multi-agent-records"].includes(code)) return "coordination";
|
|
107
107
|
if (code.includes("docker")) return "container";
|
|
108
108
|
if (code.includes("lockfile") || code.includes("package-manager")) return "package-manager";
|
|
109
109
|
if (code.includes("node") || code.includes("python") || code.includes("version") || code.includes("runtime")) return "runtime";
|
|
@@ -115,13 +115,13 @@ function environmentStepLines(code = "") {
|
|
|
115
115
|
"Read .nvmrc and the detected Node version before changing tools.",
|
|
116
116
|
"Prefer project-local version managers such as nvm, mise, or asdf.",
|
|
117
117
|
"Ask before changing global Node or npm installations.",
|
|
118
|
-
"Run aienvmp
|
|
118
|
+
"Run aienvmp checkpoint --actor agent:id --summary what-changed --target node after alignment."
|
|
119
119
|
];
|
|
120
120
|
if (code === "python-version-mismatch") return [
|
|
121
121
|
"Read .python-version and the detected Python version before changing tools.",
|
|
122
122
|
"Prefer project-local virtual environments or version managers.",
|
|
123
123
|
"Ask before changing global Python installations.",
|
|
124
|
-
"Run aienvmp
|
|
124
|
+
"Run aienvmp checkpoint --actor agent:id --summary what-changed --target python after alignment."
|
|
125
125
|
];
|
|
126
126
|
if (code === "mixed-node-lockfiles" || code === "package-manager-policy-mismatch") return [
|
|
127
127
|
"Identify the intended package manager from project policy and lockfiles.",
|
|
@@ -133,13 +133,13 @@ function environmentStepLines(code = "") {
|
|
|
133
133
|
"Confirm whether the Python project is active.",
|
|
134
134
|
"Prefer project-local Python setup before global installation.",
|
|
135
135
|
"Ask before installing a global Python runtime.",
|
|
136
|
-
"Run aienvmp
|
|
136
|
+
"Run aienvmp checkpoint --actor agent:id --summary what-changed --target python after setup."
|
|
137
137
|
];
|
|
138
138
|
if (code === "docker-missing") return [
|
|
139
139
|
"Confirm whether Docker is required for the current task.",
|
|
140
140
|
"Do not change Docker daemon or context without user approval.",
|
|
141
141
|
"Document fallback commands if Docker is unavailable.",
|
|
142
|
-
"Run aienvmp
|
|
142
|
+
"Run aienvmp checkpoint --actor agent:id --summary what-changed --target docker after Docker availability changes."
|
|
143
143
|
];
|
|
144
144
|
if (code === "manifest-stale") return [
|
|
145
145
|
"Run aienvmp sync before environment changes.",
|
|
@@ -158,10 +158,14 @@ function environmentStepLines(code = "") {
|
|
|
158
158
|
"Run aienvmp handoff --record --actor agent:id before the next AI continues.",
|
|
159
159
|
"Review recent changes before new environment work."
|
|
160
160
|
];
|
|
161
|
+
if (code === "multi-agent-records") return [
|
|
162
|
+
"Review agentActivity and recent timeline records for the shared target.",
|
|
163
|
+
"Run aienvmp checkpoint --actor agent:id --summary what-changed --target environment before more environment work."
|
|
164
|
+
];
|
|
161
165
|
return [
|
|
162
166
|
"Review the warning before changing environment state.",
|
|
163
167
|
"Ask before global environment changes.",
|
|
164
|
-
"Run aienvmp
|
|
168
|
+
"Run aienvmp checkpoint --actor agent:id --summary what-changed --target environment after any accepted change."
|
|
165
169
|
];
|
|
166
170
|
}
|
|
167
171
|
|
package/src/commands/record.js
CHANGED
|
@@ -22,7 +22,8 @@ export async function recordWorkspace(args) {
|
|
|
22
22
|
trust: changedTrust(now, requiresReview)
|
|
23
23
|
};
|
|
24
24
|
await appendJsonLine(timelinePath(dir), entry);
|
|
25
|
-
console.log(`recorded ${entry.type} by ${actor}`);
|
|
25
|
+
if (!args.quiet) console.log(`recorded ${entry.type} by ${actor}`);
|
|
26
|
+
return entry;
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
export function followUpForRecord(record = {}) {
|
|
@@ -42,6 +43,7 @@ export function followUpForRecord(record = {}) {
|
|
|
42
43
|
? "Dependency or security records should refresh the env map and handoff context."
|
|
43
44
|
: "Environment records should refresh the env map and handoff context.",
|
|
44
45
|
commands: [
|
|
46
|
+
`aienvmp checkpoint --actor agent:id --summary ${isDependency ? "dependency-change" : "what-changed"} --target ${isDependency ? "dependency" : target || "environment"}`,
|
|
45
47
|
"aienvmp sync",
|
|
46
48
|
"aienvmp status --write",
|
|
47
49
|
"aienvmp handoff --record --actor agent:id"
|
package/src/commands/status.js
CHANGED
|
@@ -25,6 +25,7 @@ export async function statusWorkspace(args) {
|
|
|
25
25
|
console.log(`next: ${output.nextCommand}`);
|
|
26
26
|
console.log(`ai: ${output.quickstart.readFirst} -> ${output.quickstart.detailCommand}`);
|
|
27
27
|
console.log(`intent: ${output.intentTargets[0]?.command || output.commands.recordIntent}`);
|
|
28
|
+
console.log(`checkpoint: ${output.commands.checkpoint}`);
|
|
28
29
|
console.log(`handoff: ${output.nextAgent.handoffCommand}`);
|
|
29
30
|
console.log(`strict: ${output.enforcement.recommendedCommand}`);
|
|
30
31
|
if (artifact) console.log(`status: ${artifact}`);
|
package/src/contract.js
CHANGED
|
@@ -4,7 +4,7 @@ export function preflightContract() {
|
|
|
4
4
|
version: 1,
|
|
5
5
|
stability: "additive",
|
|
6
6
|
requiredFields: ["schemaVersion", "state", "decision", "quickstart", "commands", "artifacts"],
|
|
7
|
-
aiEntryFields: ["state", "summary", "nextAgent", "coordination", "followUps", "dependencyReadSet", "dependencyChangeProtocol"],
|
|
7
|
+
aiEntryFields: ["state", "summary", "nextAgent", "coordination", "agentActivity", "followUps", "dependencyReadSet", "dependencyChangeProtocol"],
|
|
8
8
|
rule: "Consumers should ignore unknown fields and treat missing optional fields as unavailable."
|
|
9
9
|
};
|
|
10
10
|
}
|
package/src/decision.js
CHANGED
|
@@ -25,6 +25,7 @@ export function aiDecision(warnings = [], intents = []) {
|
|
|
25
25
|
: ["continue with project-local work", "run aienvmp intent before environment changes"],
|
|
26
26
|
requiredCommands: {
|
|
27
27
|
beforeEnvironmentChange: "aienvmp intent --actor agent:id --action planned-change --target <runtime|package-manager|docker>",
|
|
28
|
+
checkpointAfterChange: "aienvmp checkpoint --actor agent:id --summary what-changed --target environment",
|
|
28
29
|
refreshAfterChange: "aienvmp sync",
|
|
29
30
|
recordAfterChange: "aienvmp record --actor agent:id --summary what-changed",
|
|
30
31
|
handoff: "aienvmp handoff --record --actor agent:id",
|
package/src/doctor.js
CHANGED
|
@@ -46,6 +46,7 @@ export function diagnose(manifest, context = {}) {
|
|
|
46
46
|
warnings.push(...coordinationWarnings(context.intents || []));
|
|
47
47
|
warnings.push(...staleIntentWarnings(context.intents || []));
|
|
48
48
|
warnings.push(...handoffWarnings(context.timeline || []));
|
|
49
|
+
warnings.push(...multiAgentRecordWarnings(context.timeline || []));
|
|
49
50
|
return warnings;
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -108,6 +109,32 @@ export function handoffWarnings(timeline = []) {
|
|
|
108
109
|
}];
|
|
109
110
|
}
|
|
110
111
|
|
|
112
|
+
export function multiAgentRecordWarnings(timeline = []) {
|
|
113
|
+
const lastHandoffAt = lastTime(timeline, (item) => item.type === "agent-handoff");
|
|
114
|
+
const byTarget = new Map();
|
|
115
|
+
for (const item of timeline) {
|
|
116
|
+
if (!isEnvironmentChange(item)) continue;
|
|
117
|
+
if (lastHandoffAt && timeOf(item) <= lastHandoffAt) continue;
|
|
118
|
+
const actor = String(item.actor || "").trim();
|
|
119
|
+
if (!actor) continue;
|
|
120
|
+
const target = normalizeTarget(item.target || inferTarget(`${item.summary || ""} ${item.action || ""} ${item.change?.key || ""}`) || "environment");
|
|
121
|
+
const list = byTarget.get(target) || [];
|
|
122
|
+
list.push(item);
|
|
123
|
+
byTarget.set(target, list);
|
|
124
|
+
}
|
|
125
|
+
const warnings = [];
|
|
126
|
+
for (const [target, list] of byTarget) {
|
|
127
|
+
const actors = [...new Set(list.map((item) => item.actor).filter(Boolean))];
|
|
128
|
+
if (actors.length < 2) continue;
|
|
129
|
+
warnings.push({
|
|
130
|
+
code: "multi-agent-records",
|
|
131
|
+
target,
|
|
132
|
+
message: `Multiple agents recorded environment changes for ${target} after the last handoff. Run handoff and review follow-ups before continuing.`
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return warnings;
|
|
136
|
+
}
|
|
137
|
+
|
|
111
138
|
function inferTarget(action = "") {
|
|
112
139
|
const normalized = String(action).toLowerCase();
|
|
113
140
|
for (const target of ["dependency", "node", "python", "docker", "npm", "pnpm", "yarn", "uv", "pip", "pipx"]) {
|
|
@@ -117,6 +144,23 @@ function inferTarget(action = "") {
|
|
|
117
144
|
return "";
|
|
118
145
|
}
|
|
119
146
|
|
|
147
|
+
function normalizeTarget(target = "") {
|
|
148
|
+
const normalized = String(target).trim().toLowerCase();
|
|
149
|
+
if (["npm", "pnpm", "yarn"].includes(normalized)) return "package-manager";
|
|
150
|
+
if (["pip", "pipx", "uv"].includes(normalized)) return "python";
|
|
151
|
+
return normalized || "environment";
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function lastTime(items = [], predicate) {
|
|
155
|
+
const item = [...items].reverse().find(predicate);
|
|
156
|
+
return item ? timeOf(item) : 0;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function timeOf(item = {}) {
|
|
160
|
+
const value = new Date(item.at || 0).getTime();
|
|
161
|
+
return Number.isFinite(value) ? value : 0;
|
|
162
|
+
}
|
|
163
|
+
|
|
120
164
|
function isEnvironmentChange(item = {}) {
|
|
121
165
|
if (["runtime", "package-manager", "container", "dependency"].includes(item.change?.scope)) return true;
|
|
122
166
|
if (item.type === "detected-change") return false;
|
package/src/enforcement.js
CHANGED
|
@@ -67,6 +67,6 @@ function warningMatchesScope(warning, scope) {
|
|
|
67
67
|
|
|
68
68
|
function warningScope(code = "") {
|
|
69
69
|
if (code === "security-vulnerabilities") return "security";
|
|
70
|
-
if (["conflicting-open-intents", "stale-open-intent", "handoff-stale"].includes(code)) return "coordination";
|
|
70
|
+
if (["conflicting-open-intents", "stale-open-intent", "handoff-stale", "multi-agent-records"].includes(code)) return "coordination";
|
|
71
71
|
return "policy";
|
|
72
72
|
}
|
package/src/manifest.js
CHANGED
|
@@ -42,7 +42,8 @@ export async function buildManifest(dir, options = {}) {
|
|
|
42
42
|
handoffCommand: "aienvmp handoff",
|
|
43
43
|
intentCommand: "aienvmp intent --actor agent:id --action planned-change",
|
|
44
44
|
recordCommand: "aienvmp record --actor agent:id --summary what-changed",
|
|
45
|
-
|
|
45
|
+
checkpointCommand: "aienvmp checkpoint --actor agent:id --summary what-changed --target environment",
|
|
46
|
+
afterEnvironmentChange: ["aienvmp checkpoint --actor agent:id --summary what-changed --target environment"],
|
|
46
47
|
trustModel: {
|
|
47
48
|
agentWritable: ["observed", "planned", "changed", "review", "stale"],
|
|
48
49
|
verifiedRequires: "human-or-ci",
|
package/src/preflight.js
CHANGED
|
@@ -15,6 +15,7 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
|
|
|
15
15
|
const dependencyChangeProtocol = dependencyProtocol(manifest, dependencyReadSet);
|
|
16
16
|
const coordination = coordinationSummary(intents);
|
|
17
17
|
const followUps = pendingFollowUps(timeline);
|
|
18
|
+
const agentActivity = agentActivitySummary(timeline);
|
|
18
19
|
return {
|
|
19
20
|
schemaVersion: 1,
|
|
20
21
|
contract: preflightContract(),
|
|
@@ -55,6 +56,7 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
|
|
|
55
56
|
quickstart: agentQuickstart(decision.reviewRequired),
|
|
56
57
|
nextAgent: nextAgentHint(state, dependencyReadSet, dependencyChangeProtocol),
|
|
57
58
|
coordination,
|
|
59
|
+
agentActivity,
|
|
58
60
|
followUps,
|
|
59
61
|
intentTargets,
|
|
60
62
|
dependencyReadSet,
|
|
@@ -74,6 +76,7 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
|
|
|
74
76
|
context: "aienvmp context --json",
|
|
75
77
|
plan: "aienvmp plan --write",
|
|
76
78
|
handoff: "aienvmp handoff --record --actor agent:id",
|
|
79
|
+
checkpoint: "aienvmp checkpoint --actor agent:id --summary what-changed --target environment",
|
|
77
80
|
recordIntent: intentTargets[0]?.command || "aienvmp intent --actor agent:id --action planned-change"
|
|
78
81
|
},
|
|
79
82
|
topAction,
|
|
@@ -81,6 +84,42 @@ export function buildPreflight(manifest = {}, warnings = [], intents = [], timel
|
|
|
81
84
|
};
|
|
82
85
|
}
|
|
83
86
|
|
|
87
|
+
function agentActivitySummary(timeline = []) {
|
|
88
|
+
const lastHandoffAt = lastTime(timeline, (item) => item.type === "agent-handoff");
|
|
89
|
+
const envRecords = timeline
|
|
90
|
+
.filter((item) => isEnvironmentRecord(item))
|
|
91
|
+
.filter((item) => !lastHandoffAt || timeOf(item) > lastHandoffAt);
|
|
92
|
+
const byTarget = new Map();
|
|
93
|
+
for (const item of envRecords) {
|
|
94
|
+
const target = normalizeTarget(item.target || targetFromText(`${item.summary || ""} ${item.action || ""}`) || "environment") || "environment";
|
|
95
|
+
const summary = byTarget.get(target) || { target, count: 0, actors: [], latest: null, multiActor: false };
|
|
96
|
+
summary.count += 1;
|
|
97
|
+
if (item.actor && !summary.actors.includes(item.actor)) summary.actors.push(item.actor);
|
|
98
|
+
if (!summary.latest || timeOf(item) > timeOf(summary.latest)) summary.latest = item;
|
|
99
|
+
byTarget.set(target, summary);
|
|
100
|
+
}
|
|
101
|
+
const targets = [...byTarget.values()].map((item) => ({
|
|
102
|
+
target: item.target,
|
|
103
|
+
count: item.count,
|
|
104
|
+
actors: item.actors.slice(0, 5),
|
|
105
|
+
latestAt: item.latest?.at || "",
|
|
106
|
+
latestSummary: item.latest?.summary || item.latest?.action || item.latest?.type || "",
|
|
107
|
+
multiActor: item.actors.length > 1
|
|
108
|
+
}));
|
|
109
|
+
const multiActorTargets = targets.filter((item) => item.multiActor).map((item) => item.target);
|
|
110
|
+
return {
|
|
111
|
+
sinceLastHandoff: lastHandoffAt ? "after-last-handoff" : "all-recorded",
|
|
112
|
+
environmentRecordCount: envRecords.length,
|
|
113
|
+
targets,
|
|
114
|
+
multiActorTargets,
|
|
115
|
+
next: multiActorTargets.length
|
|
116
|
+
? "Run handoff and review follow-ups before another environment change."
|
|
117
|
+
: envRecords.length
|
|
118
|
+
? "Run handoff before another AI continues environment work."
|
|
119
|
+
: "No environment records need coordination."
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
84
123
|
function coordinationSummary(intents = []) {
|
|
85
124
|
const byTarget = new Map();
|
|
86
125
|
for (const intent of intents) {
|
|
@@ -111,6 +150,41 @@ function coordinationSummary(intents = []) {
|
|
|
111
150
|
};
|
|
112
151
|
}
|
|
113
152
|
|
|
153
|
+
function isEnvironmentRecord(item = {}) {
|
|
154
|
+
if (item.type === "detected-change") return false;
|
|
155
|
+
const text = `${item.type || ""} ${item.target || ""} ${item.summary || ""} ${item.action || ""} ${item.change?.scope || ""} ${item.change?.key || ""}`.toLowerCase();
|
|
156
|
+
return [
|
|
157
|
+
"dependency",
|
|
158
|
+
"dependencies",
|
|
159
|
+
"package",
|
|
160
|
+
"lockfile",
|
|
161
|
+
"vulnerability",
|
|
162
|
+
"runtime",
|
|
163
|
+
"node",
|
|
164
|
+
"python",
|
|
165
|
+
"docker",
|
|
166
|
+
"package manager",
|
|
167
|
+
"package-manager",
|
|
168
|
+
"npm",
|
|
169
|
+
"pnpm",
|
|
170
|
+
"yarn",
|
|
171
|
+
"uv",
|
|
172
|
+
"pip",
|
|
173
|
+
"pipx",
|
|
174
|
+
"global"
|
|
175
|
+
].some((token) => text.includes(token));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function lastTime(items = [], predicate) {
|
|
179
|
+
const item = [...items].reverse().find(predicate);
|
|
180
|
+
return item ? timeOf(item) : 0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function timeOf(item = {}) {
|
|
184
|
+
const value = new Date(item.at || 0).getTime();
|
|
185
|
+
return Number.isFinite(value) ? value : 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
114
188
|
function nextAgentHint(state, dependencyReadSet = [], dependencyChangeProtocol = {}) {
|
|
115
189
|
const firstDependency = dependencyReadSet[0];
|
|
116
190
|
const dependencyFiles = firstDependency
|
|
@@ -147,6 +221,7 @@ function dependencyProtocol(manifest = {}, dependencyReadSet = []) {
|
|
|
147
221
|
recordIntent: "aienvmp intent --actor agent:id --action planned-change --target dependency",
|
|
148
222
|
refreshAfterChange: "aienvmp sync",
|
|
149
223
|
recordAfterChange: "aienvmp record --actor agent:id --summary dependency-change --target dependency",
|
|
224
|
+
checkpointAfterChange: "aienvmp checkpoint --actor agent:id --summary dependency-change --target dependency",
|
|
150
225
|
handoff: "aienvmp handoff --record --actor agent:id"
|
|
151
226
|
},
|
|
152
227
|
mustNotDo: [
|
|
@@ -262,7 +337,8 @@ function agentQuickstart(reviewRequired) {
|
|
|
262
337
|
readFirst: "aienvmp status --write",
|
|
263
338
|
detailCommand: "aienvmp context --json",
|
|
264
339
|
beforeEnvironmentChange: "aienvmp intent --actor agent:id --action planned-change --target <runtime|package-manager|docker|dependency>",
|
|
265
|
-
afterEnvironmentChange: "aienvmp
|
|
340
|
+
afterEnvironmentChange: "aienvmp checkpoint --actor agent:id --summary what-changed --target environment",
|
|
341
|
+
checkpointAfterChange: "aienvmp checkpoint --actor agent:id --summary what-changed --target environment",
|
|
266
342
|
handoff: "aienvmp handoff --record --actor agent:id",
|
|
267
343
|
rule: reviewRequired
|
|
268
344
|
? "Review warnings or open intents before environment changes; project-local work may continue."
|
package/src/render.js
CHANGED
|
@@ -13,9 +13,7 @@ export function renderAIEnv(manifest, timeline = [], warnings = [], intents = []
|
|
|
13
13
|
lines.push("2. Prefer project-local version files such as `.nvmrc`, `.python-version`, `mise.toml`, and `.tool-versions`.");
|
|
14
14
|
lines.push("3. Ask the user before changing global environment state.");
|
|
15
15
|
lines.push("4. Record planned environment changes with `aienvmp intent --actor agent:id --action planned-change`.");
|
|
16
|
-
lines.push("5. After environment changes, run `aienvmp
|
|
17
|
-
lines.push("6. Record what changed with `aienvmp record --actor agent:id --summary what-changed`.");
|
|
18
|
-
lines.push("7. At handoff, run `aienvmp handoff --record --actor agent:id`.", "");
|
|
16
|
+
lines.push("5. After environment changes, run `aienvmp checkpoint --actor agent:id --summary what-changed --target environment`.", "");
|
|
19
17
|
lines.push(...preflightLines(manifest.preflight), "");
|
|
20
18
|
lines.push("## Current Policy", "");
|
|
21
19
|
lines.push(...policyLines(policy));
|
|
@@ -107,7 +105,7 @@ function preflightLines(preflight = {}) {
|
|
|
107
105
|
lines.push(`- Mode: ${dependencyProtocol.mode}`);
|
|
108
106
|
lines.push(`- Package manager policy: ${dependencyProtocol.packageManagerPolicy}`);
|
|
109
107
|
lines.push(`- Intent: \`${dependencyProtocol.commands.recordIntent}\``);
|
|
110
|
-
lines.push(`- After change: \`${dependencyProtocol.commands.
|
|
108
|
+
lines.push(`- After change: \`${dependencyProtocol.commands.checkpointAfterChange || dependencyProtocol.commands.recordAfterChange}\``);
|
|
111
109
|
lines.push(`- Handoff: \`${dependencyProtocol.commands.handoff}\``);
|
|
112
110
|
for (const item of dependencyProtocol.mustNotDo.slice(0, 3)) lines.push(`- Must not: ${item}`);
|
|
113
111
|
}
|
|
@@ -127,8 +125,7 @@ Before changing runtimes, package managers, Docker settings, global packages, or
|
|
|
127
125
|
3. Read \`AIENV.md\`.
|
|
128
126
|
4. If status or context says \`review-required\`, ask the user before changing the environment.
|
|
129
127
|
5. Record planned environment changes with the recommended target, for example \`aienvmp intent --actor agent:id --action planned-change --target dependency\`.
|
|
130
|
-
6. After environment changes, run \`aienvmp
|
|
131
|
-
7. At handoff, run \`aienvmp handoff --record --actor agent:id\`.
|
|
128
|
+
6. After environment changes, run \`aienvmp checkpoint --actor agent:id --summary what-changed --target environment\`.
|
|
132
129
|
|
|
133
130
|
\`aienvmp\` does not replace this instruction file. It provides the live env map, lightweight runtime SBOM, intent log, timeline, and dashboard.`;
|
|
134
131
|
}
|
|
@@ -160,9 +157,7 @@ export function renderContext(manifest, timeline = [], warnings = [], intents =
|
|
|
160
157
|
"- Treat policy mismatches as review-required, not as permission to break ongoing operations.",
|
|
161
158
|
"- Prefer project-local version files and local environments.",
|
|
162
159
|
"- Before planned env changes, run `aienvmp intent --actor agent:id --action planned-change`.",
|
|
163
|
-
"- After env changes, run `aienvmp
|
|
164
|
-
"- Then run `aienvmp record --actor agent:id --summary what-changed`.",
|
|
165
|
-
"- Before handing work to another AI, run `aienvmp handoff --record --actor agent:id`.",
|
|
160
|
+
"- After env changes, run `aienvmp checkpoint --actor agent:id --summary what-changed --target environment`.",
|
|
166
161
|
"",
|
|
167
162
|
"Warnings:",
|
|
168
163
|
...(warnings.length ? warnings.map((w) => `- ${w.message}`) : ["- none"]),
|
|
@@ -176,6 +171,9 @@ export function renderContext(manifest, timeline = [], warnings = [], intents =
|
|
|
176
171
|
"Follow-ups:",
|
|
177
172
|
...followUpLines(manifest.preflight?.followUps),
|
|
178
173
|
"",
|
|
174
|
+
"Agent activity:",
|
|
175
|
+
...agentActivityLines(manifest.preflight?.agentActivity),
|
|
176
|
+
"",
|
|
179
177
|
"Open intents:",
|
|
180
178
|
...(intents.length ? intents.slice(-5).reverse().map((i) => `- ${i.actor}: ${i.action}`) : ["- none"]),
|
|
181
179
|
"",
|
|
@@ -208,6 +206,9 @@ export function renderHandoff(handoff) {
|
|
|
208
206
|
"Coordination:",
|
|
209
207
|
...coordinationHandoffLines(handoff.coordination),
|
|
210
208
|
"",
|
|
209
|
+
"Agent activity:",
|
|
210
|
+
...agentActivityLines(handoff.agentActivity),
|
|
211
|
+
"",
|
|
211
212
|
"Warnings:",
|
|
212
213
|
...(handoff.warnings.length ? handoff.warnings.map((w) => `- ${w.message}`) : ["- none"]),
|
|
213
214
|
"",
|
|
@@ -248,7 +249,7 @@ function dependencyHandoffLines(dependencyHandoff = {}) {
|
|
|
248
249
|
lines.push("- Read: no dependency files detected");
|
|
249
250
|
}
|
|
250
251
|
lines.push(`- Intent: ${protocol.recordIntent || "aienvmp intent --actor agent:id --action planned-change --target dependency"}`);
|
|
251
|
-
lines.push(`- After change: ${protocol.recordAfterChange || "aienvmp
|
|
252
|
+
lines.push(`- After change: ${protocol.checkpointAfterChange || protocol.recordAfterChange || "aienvmp checkpoint --actor agent:id --summary dependency-change --target dependency"}`);
|
|
252
253
|
lines.push(`- Handoff: ${protocol.handoff || "aienvmp handoff --record --actor agent:id"}`);
|
|
253
254
|
return lines;
|
|
254
255
|
}
|
|
@@ -298,7 +299,7 @@ function dependencyProtocolPlanLines(protocol = {}) {
|
|
|
298
299
|
`- Mode: ${protocol.mode || "advisory"}`,
|
|
299
300
|
`- Package manager policy: ${protocol.packageManagerPolicy || "not-detected"}`,
|
|
300
301
|
`- Intent: ${protocol.commands.recordIntent}`,
|
|
301
|
-
`- After change: ${protocol.commands.refreshAfterChange}; ${protocol.commands.recordAfterChange}`,
|
|
302
|
+
`- After change: ${protocol.commands.checkpointAfterChange || `${protocol.commands.refreshAfterChange}; ${protocol.commands.recordAfterChange}`}`,
|
|
302
303
|
...(protocol.mustNotDo || []).slice(0, 3).map((item) => `- Must not: ${item}`)
|
|
303
304
|
];
|
|
304
305
|
}
|
|
@@ -320,6 +321,16 @@ function followUpLines(followUps = []) {
|
|
|
320
321
|
});
|
|
321
322
|
}
|
|
322
323
|
|
|
324
|
+
function agentActivityLines(activity = {}) {
|
|
325
|
+
const targets = activity.targets || [];
|
|
326
|
+
if (!targets.length) return ["- none"];
|
|
327
|
+
return targets.slice(0, 5).map((item) => {
|
|
328
|
+
const actors = item.actors?.length ? item.actors.join(", ") : "unknown";
|
|
329
|
+
const flag = item.multiActor ? "multi-agent" : "single-agent";
|
|
330
|
+
return `- ${item.target}: ${item.count} record(s), ${actors}, ${flag}${item.latestSummary ? ` - ${item.latestSummary}` : ""}`;
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
|
|
323
334
|
function environmentLines(item) {
|
|
324
335
|
return [
|
|
325
336
|
`- ${item.category}: ${item.summary}`,
|
|
@@ -448,10 +459,13 @@ const intentTargets=manifest.preflight?.intentTargets||[];
|
|
|
448
459
|
const intentTargetsHtml=intentTargets.length?'<div class="timeline">'+intentTargets.slice(0,5).map(t=>\`<div class="event"><time>\${esc(t.target)}</time><div><b>\${esc(t.target)}</b> \${esc(t.reason||'Record this target before environment changes.')}\${t.command?\`<div class="path">\${esc(t.command)}</div>\`:''}</div></div>\`).join('')+'</div>':'<div class="okline">No specific target recommendation. Use <code>aienvmp intent --actor agent:id --action planned-change</code>.</div>';
|
|
449
460
|
const followUps=manifest.preflight?.followUps||[];
|
|
450
461
|
const followUpsHtml=followUps.length?'<div class="timeline">'+followUps.slice(0,5).map(f=>\`<div class="event"><time>\${esc(f.target||'env')}</time><div><b>\${esc(f.summary||'follow-up')}</b> \${esc(f.reason||'Refresh shared context.')}\${f.commands?.length?\`<div class="path">\${esc(f.commands.join(' -> '))}</div>\`:''}</div></div>\`).join('')+'</div>':'<div class="okline">No pending follow-ups after environment records.</div>';
|
|
462
|
+
const agentActivity=manifest.preflight?.agentActivity||{};
|
|
463
|
+
const activityTargets=agentActivity.targets||[];
|
|
464
|
+
const activityHtml=activityTargets.length?'<div class="timeline">'+activityTargets.slice(0,5).map(a=>\`<div class="event"><time>\${esc(a.target||'env')}</time><div><b>\${esc((a.actors||[]).join(', ')||'unknown')}</b> \${esc(a.count||0)} record(s) \${a.multiActor?'<code>multi-agent</code>':'<code>single-agent</code>'}\${a.latestSummary?\`<div class="path">\${esc(a.latestSummary)}</div>\`:''}</div></div>\`).join('')+'</div><div class="path">'+esc(agentActivity.next||'Review activity before environment changes.')+'</div>':'<div class="okline">No recorded environment activity needs coordination.</div>';
|
|
451
465
|
const dependencyReadSet=manifest.preflight?.dependencyReadSet||[];
|
|
452
466
|
const dependencyReadSetHtml=dependencyReadSet.length?'<div class="timeline">'+dependencyReadSet.slice(0,5).map(d=>\`<div class="event"><time>\${esc(d.ecosystem||'deps')}</time><div><b>\${esc(d.manifest||'dependency files')}</b> <code>\${esc(d.manager||'unknown')}</code><div class="path">\${esc([d.manifest,...(d.lockfiles||[])].filter(Boolean).join(', '))}</div>\${d.riskPackages?.length?\`<div class="path">risk: \${esc(d.riskPackages.join(', '))}</div>\`:''}</div></div>\`).join('')+'</div>':'<div class="okline">No dependency files detected.</div>';
|
|
453
467
|
const dependencyProtocol=manifest.preflight?.dependencyChangeProtocol||{};
|
|
454
|
-
const dependencyProtocolHtml=dependencyProtocol.commands?'<table><tr><th>Mode</th><td><code>'+esc(dependencyProtocol.mode||'advisory')+'</code></td></tr><tr><th>Policy</th><td><code>'+esc(dependencyProtocol.packageManagerPolicy||'not-detected')+'</code></td></tr><tr><th>Intent</th><td><code>'+esc(dependencyProtocol.commands.recordIntent)+'</code></td></tr><tr><th>After</th><td><code>'+esc(dependencyProtocol.commands.
|
|
468
|
+
const dependencyProtocolHtml=dependencyProtocol.commands?'<table><tr><th>Mode</th><td><code>'+esc(dependencyProtocol.mode||'advisory')+'</code></td></tr><tr><th>Policy</th><td><code>'+esc(dependencyProtocol.packageManagerPolicy||'not-detected')+'</code></td></tr><tr><th>Intent</th><td><code>'+esc(dependencyProtocol.commands.recordIntent)+'</code></td></tr><tr><th>After</th><td><code>'+esc(dependencyProtocol.commands.checkpointAfterChange||dependencyProtocol.commands.recordAfterChange)+'</code></td></tr></table><div class="timeline">'+(dependencyProtocol.mustNotDo||[]).slice(0,3).map(item=>\`<div class="event"><time>avoid</time><div>\${esc(item)}</div></div>\`).join('')+'</div>':'<div class="okline">No dependency change protocol available.</div>';
|
|
455
469
|
const card=(title,badge,body)=>\`<section class="card"><div class="card-head"><h2>\${title}</h2>\${badge||''}</div>\${body}</section>\`;
|
|
456
470
|
const reviewRequired=warnings.length>0||intents.length>0;
|
|
457
471
|
const recentChanges=timeline.slice(-8).length;
|
|
@@ -504,6 +518,8 @@ document.getElementById('app').innerHTML=\`
|
|
|
504
518
|
<div style="height:14px"></div>
|
|
505
519
|
\${card('Follow-ups',followUps.length?'<span class="pill warn">'+followUps.length+' pending</span>':'<span class="pill">clear</span>',followUpsHtml)}
|
|
506
520
|
<div style="height:14px"></div>
|
|
521
|
+
\${card('Agent Activity',agentActivity.multiActorTargets?.length?'<span class="pill warn">'+agentActivity.multiActorTargets.length+' shared</span>':'<span class="pill">clear</span>',activityHtml)}
|
|
522
|
+
<div style="height:14px"></div>
|
|
507
523
|
\${card('AI Contract','<span class="pill">'+(contract.stability||'additive')+'</span>',contractHtml)}
|
|
508
524
|
<div style="height:14px"></div>
|
|
509
525
|
\${card('Dependency Read Set','<span class="pill">'+dependencyReadSet.length+' files</span>',dependencyReadSetHtml)}
|