mindforge-cc 11.4.0 → 11.5.1
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/.agent/CLAUDE.md +13 -0
- package/.agent/hooks/lib/hook-flags.js +78 -0
- package/.agent/hooks/lib/pretooluse-visible-output.js +46 -0
- package/.agent/hooks/mindforge-block-no-verify.js +552 -0
- package/.agent/hooks/mindforge-config-protection.js +144 -0
- package/.agent/hooks/run-with-flags.js +207 -0
- package/.agent/mindforge/checkpoint.md +76 -0
- package/.agent/mindforge/harness-audit.md +59 -0
- package/.agent/mindforge/instinct.md +46 -0
- package/.agent/mindforge/orch-add-feature.md +43 -0
- package/.agent/mindforge/orch-build-mvp.md +48 -0
- package/.agent/mindforge/orch-change-feature.md +45 -0
- package/.agent/mindforge/orch-fix-defect.md +43 -0
- package/.agent/mindforge/orch-refine-code.md +43 -0
- package/.claude/CLAUDE.md +13 -0
- package/.claude/commands/mindforge/checkpoint.md +76 -0
- package/.claude/commands/mindforge/execute-phase.md +47 -6
- package/.claude/commands/mindforge/harness-audit.md +59 -0
- package/.claude/commands/mindforge/instinct.md +46 -0
- package/.claude/commands/mindforge/orch-add-feature.md +43 -0
- package/.claude/commands/mindforge/orch-build-mvp.md +48 -0
- package/.claude/commands/mindforge/orch-change-feature.md +45 -0
- package/.claude/commands/mindforge/orch-fix-defect.md +43 -0
- package/.claude/commands/mindforge/orch-refine-code.md +43 -0
- package/.claude/commands/mindforge/plan-write.md +11 -0
- package/.claude/commands/mindforge/product-spec.md +76 -0
- package/.mindforge/config.json +2 -2
- package/.mindforge/engine/instincts/instinct-schema.md +17 -9
- package/.mindforge/imported-agents.jsonl +10 -0
- package/.mindforge/manifests/install-components.json +36 -0
- package/.mindforge/manifests/install-modules.json +193 -0
- package/.mindforge/manifests/install-profiles.json +57 -0
- package/.mindforge/memory/sync-manifest.json +1 -1
- package/.mindforge/personas/gan-evaluator.md +226 -0
- package/.mindforge/personas/gan-generator.md +151 -0
- package/.mindforge/personas/gan-planner.md +118 -0
- package/.mindforge/personas/harness-optimizer.md +55 -0
- package/.mindforge/personas/loop-operator.md +58 -0
- package/.mindforge/schemas/hooks.schema.json +199 -0
- package/.mindforge/schemas/install-modules.schema.json +44 -0
- package/.mindforge/schemas/install-state.schema.json +95 -0
- package/.mindforge/schemas/plugin.schema.json +75 -0
- package/.mindforge/schemas/provenance.schema.json +31 -0
- package/.mindforge/skills/agent-architecture-audit/SKILL.md +272 -0
- package/.mindforge/skills/continuous-learning/SKILL.md +16 -0
- package/.mindforge/skills/orch-pipeline/SKILL.md +284 -0
- package/.mindforge/skills/writing-plans/SKILL.md +76 -0
- package/CHANGELOG.md +120 -0
- package/MINDFORGE.md +3 -3
- package/README.md +0 -1
- package/RELEASENOTES.md +131 -0
- package/SECURITY.md +16 -0
- package/bin/autonomous/auto-runner.js +46 -5
- package/bin/autonomous/handoff-schema.js +114 -0
- package/bin/autonomous/session-guardian.sh +138 -0
- package/bin/autonomous/supervisor.js +98 -0
- package/bin/change-classifier.js +19 -5
- package/bin/dashboard/api-router.js +10 -1
- package/bin/governance/approve.js +65 -28
- package/bin/governance/config-manager.js +3 -1
- package/bin/governance/rbac-manager.js +14 -6
- package/bin/harness-audit.js +520 -0
- package/bin/hooks/instinct-capture-hook.js +16 -1
- package/bin/hooks/lib/detect-project.js +72 -0
- package/bin/installer/harness-adapter-compliance.js +321 -0
- package/bin/installer/install-manifests.js +200 -0
- package/bin/installer/install-state.js +243 -0
- package/bin/installer-core.js +1 -1
- package/bin/learning/instinct-cli.js +359 -0
- package/bin/learning/lib/ssrf-guard.js +252 -0
- package/bin/memory/eis-client.js +31 -10
- package/bin/memory/federated-sync.js +11 -2
- package/bin/memory/knowledge-capture.js +10 -1
- package/bin/memory/pillar-health-tracker.js +9 -1
- package/bin/models/llm-errors.js +79 -0
- package/bin/models/model-client.js +39 -4
- package/bin/models/ollama-provider.js +115 -0
- package/bin/models/openai-provider.js +40 -9
- package/bin/models/profiles-loader.js +147 -0
- package/bin/models/provider-registry.js +59 -0
- package/bin/review/ads-engine.js +2 -2
- package/bin/revops/market-evaluator.js +23 -2
- package/bin/revops/router-steering-v2.js +17 -2
- package/bin/security/trust-boundaries.js +20 -3
- package/bin/utils/readiness-gate.js +169 -0
- package/bin/worktree/engine.js +497 -0
- package/package.json +8 -2
- package/subagents/categories/04-quality-security/.claude-plugin/plugin.json +10 -0
- package/subagents/categories/04-quality-security/go-build-resolver.md +105 -0
- package/subagents/categories/04-quality-security/go-reviewer.md +87 -0
- package/subagents/categories/04-quality-security/python-reviewer.md +109 -0
- package/subagents/categories/04-quality-security/react-build-resolver.md +215 -0
- package/subagents/categories/04-quality-security/react-reviewer.md +167 -0
- package/subagents/categories/04-quality-security/rust-build-resolver.md +159 -0
- package/subagents/categories/04-quality-security/rust-reviewer.md +105 -0
- package/subagents/categories/04-quality-security/silent-failure-hunter.md +67 -0
- package/subagents/categories/04-quality-security/type-design-analyzer.md +58 -0
- package/subagents/categories/04-quality-security/typescript-reviewer.md +126 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,125 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [11.5.1] - 2026-06-11 — Robustness + governance-gate patch (Wave 8)
|
|
4
|
+
|
|
5
|
+
A fast-follow patch from a fresh adversarial audit of the shipped v11.5.0 tree.
|
|
6
|
+
Hardens crash-prone JSON parsing in the autonomous/memory pipelines, closes a
|
|
7
|
+
CI governance-gate gap, and tightens two security surfaces. No new features.
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **Crash-proof AUDIT.jsonl parsing** (`bin/memory/pillar-health-tracker.js`) —
|
|
12
|
+
`summarizePhase()` parsed every audit line with an unguarded `JSON.parse`, so a
|
|
13
|
+
single malformed/torn line crashed the knowledge-capture pipeline. Now parses
|
|
14
|
+
per-line in try/catch and skips bad lines.
|
|
15
|
+
- **Crash-proof compaction capture** (`bin/memory/knowledge-capture.js`) — a
|
|
16
|
+
malformed `handoff.json` no longer throws out of `captureFromCompaction()`; it
|
|
17
|
+
logs and returns `[]`, mirroring the missing-file path.
|
|
18
|
+
- **Resilient federated-sync stats** (`bin/memory/federated-sync.js`) — the two
|
|
19
|
+
unguarded `JSON.parse` calls on `sync-stats.json` (`handleSyncFailure`,
|
|
20
|
+
`resetFailures`) now fall back to `{failures:0}` on corruption, matching the
|
|
21
|
+
sibling `getLastSyncTimestamp` pattern.
|
|
22
|
+
|
|
23
|
+
### Security
|
|
24
|
+
|
|
25
|
+
- **CI Tier-3 governance gate now validates content** (`.github/workflows/control-plane.yml`)
|
|
26
|
+
— the gate counted approval files but never checked them; it now requires at
|
|
27
|
+
least one approval with `identity_verification.verified === true` and a
|
|
28
|
+
signature, and rejects any unverified/empty file. Completes the Wave-6
|
|
29
|
+
fail-closed `approve.js` work (a hand-committed empty approval no longer passes).
|
|
30
|
+
- **Dashboard approval attribution** (`bin/dashboard/api-router.js`) —
|
|
31
|
+
`POST /api/approve/:id` no longer records the client-supplied `approver`
|
|
32
|
+
(forgeable audit identity); it attributes the action to a fixed authenticated
|
|
33
|
+
actor. The dashboard remains localhost-bound + token-gated.
|
|
34
|
+
- **Destructive-command detector blocks Unix `truncate`** (`bin/security/trust-boundaries.js`)
|
|
35
|
+
— the SQL-only `truncate table` pattern missed `truncate -s 0 <path>` (in-place
|
|
36
|
+
file zeroing). Added a size-flag pattern so it is gated; benign uses stay allowed.
|
|
37
|
+
- **CI Tier-3 gate accepts an explicitly-acknowledged unverified approval**
|
|
38
|
+
(`.github/workflows/control-plane.yml`, `bin/governance/approve.js`) — since this
|
|
39
|
+
repo has no GPG signing infra, the gate accepts an approval that is either
|
|
40
|
+
GPG-verified OR an opted-in `unverified_ack` record (`approve.js` under
|
|
41
|
+
`MINDFORGE_ALLOW_UNVERIFIED_APPROVAL=1`), while still rejecting bare/stale
|
|
42
|
+
`verified:false` files. Replaced the stale v11.4.0 approval with a fresh one.
|
|
43
|
+
- **`uuid` dependency removed from `ads-engine`** (`bin/review/ads-engine.js`) — it
|
|
44
|
+
required the uninstalled `uuid` package, making `ads-engine` and (transitively)
|
|
45
|
+
`federated-sync` un-loadable in a clean install. Swapped to the built-in
|
|
46
|
+
`crypto.randomUUID()` (zero-native-deps); both modules now load.
|
|
47
|
+
|
|
48
|
+
## [11.5.0] - 2026-06-11 — Governance hardening + autonomous-engine repair (Waves 4–7)
|
|
49
|
+
|
|
50
|
+
This release bundles four waves of work: orchestration primitives, an **inert** manifest
|
|
51
|
+
engine + the new instinct CLI + GAN harness personas, governance/security hardening, and a
|
|
52
|
+
repair that makes the autonomous engine (`/mindforge:auto`) actually functional. Several
|
|
53
|
+
items ship deliberately **inert** (scaffolding present, not wired into any live path) and
|
|
54
|
+
are flagged as such below — they introduce **no behavior change yet**.
|
|
55
|
+
|
|
56
|
+
### Added
|
|
57
|
+
|
|
58
|
+
- **Instinct CLI** (`/mindforge:instinct`, `bin/learning/instinct-cli.js`) — deterministic,
|
|
59
|
+
no-LLM management of the JSONL instinct store: `list`, `export`, `import`,
|
|
60
|
+
`promote-candidates` (list + flag), and `prune`.
|
|
61
|
+
- **Typed Inter-Agent Message Protocol** (`bin/autonomous/handoff-schema.js`) — five message
|
|
62
|
+
kinds (`task_handoff`, `query`, `response`, `completed`, `conflict`) with priority levels
|
|
63
|
+
(`low`/`normal`/`high`/`critical`) and message validation. Internal orchestration primitive.
|
|
64
|
+
- **Manifest-driven install resolver** (`bin/installer/install-manifests.js`,
|
|
65
|
+
`.mindforge/manifests/install-*.json`) — profile-to-module expansion and dependency
|
|
66
|
+
detection. Ships **INERT**: the adapter wiring it into `bin/installer-core.js`'s live
|
|
67
|
+
`install()` path is deferred to a future PR. No behavior change.
|
|
68
|
+
- **GAN-style harness personas** (`.mindforge/personas/gan-evaluator.md`, `gan-generator.md`,
|
|
69
|
+
`gan-planner.md`) — fully scoped and documented. Ships **INERT**: not wired to any live
|
|
70
|
+
command or automated workflow. No behavior change.
|
|
71
|
+
- **Governance test coverage** — `tests/trust-verifier.test.js` (7 tests) and
|
|
72
|
+
`tests/rbac-manager.test.js` (8 tests) lock the fail-closed contracts for future
|
|
73
|
+
safety-critical governance changes.
|
|
74
|
+
|
|
75
|
+
### Fixed
|
|
76
|
+
|
|
77
|
+
- **SSRF import-URL guard** (`bin/learning/lib/ssrf-guard.js`) — closed an IPv6 link-local
|
|
78
|
+
bypass (`fe81`–`fe8f` reachable via string-prefix check) and a symlink path-traversal
|
|
79
|
+
bypass, using numeric bitmask validation and canonical-path checking before system-dir
|
|
80
|
+
validation.
|
|
81
|
+
- **Federated EIS sync** (`bin/memory/eis-client.js`) — `getAuthHeader` no longer throws on
|
|
82
|
+
every call; it now registers a node identity via `ZTAI.registerAgent` and signs with
|
|
83
|
+
`ZTAI.signData`, restoring sync to non-localhost EIS endpoints.
|
|
84
|
+
- **RBAC tier elevation** (`bin/governance/rbac-manager.js`) — `getRolesByTier` now fails
|
|
85
|
+
safely for unregistered agents (no thrown exception) and resolves agent tier via the
|
|
86
|
+
correct ZTAI API.
|
|
87
|
+
- **Autonomous engine — wave crash** (`bin/autonomous/auto-runner.js`) — replaced ZTAI
|
|
88
|
+
singleton misuse (`getIdentity()`, non-existent) with `_getRunnerIdentity()` using the
|
|
89
|
+
real `registerAgent` API; `/mindforge:auto` no longer crashes on every wave.
|
|
90
|
+
- **Autonomous policy gate — fail-open** (`bin/autonomous/auto-runner.js`) — the async
|
|
91
|
+
policy verdict is now `await`ed (`this.policyEngine.evaluate(intent)`); the per-wave gate
|
|
92
|
+
previously always allowed.
|
|
93
|
+
- **Autonomous engine — fail-closed identity** (`bin/autonomous/auto-runner.js`) — if runner
|
|
94
|
+
identity cannot be established, the policy gate now denies and audits (`auto_mode_denied`)
|
|
95
|
+
instead of proceeding ungoverned.
|
|
96
|
+
|
|
97
|
+
### Changed
|
|
98
|
+
|
|
99
|
+
- **Instinct store schema** (`.mindforge/engine/instincts/instinct-schema.md`) — added
|
|
100
|
+
`project_id` (stable scope key) and `source` (`auto-capture`/`manual`/`imported`/
|
|
101
|
+
`observer`), plus origin-based confidence scoring: auto-capture starts at `0.3`, manual
|
|
102
|
+
at `0.7`.
|
|
103
|
+
- **Cost-routing shadow mode** (`bin/revops/router-steering-v2.js`) — arbitrage steering now
|
|
104
|
+
respects `cost_routing.shadow_mode` (default `true`); in observe-only mode `steer()`
|
|
105
|
+
returns `{ shadow, authoritative: false }` with SHADOW vs LIVE logging.
|
|
106
|
+
|
|
107
|
+
### Security
|
|
108
|
+
|
|
109
|
+
- **Tier-3 approvals now fail closed** (`bin/governance/approve.js`) — approvals require GPG
|
|
110
|
+
verification and **throw before writing any record** when no GPG key is configured, unless
|
|
111
|
+
`MINDFORGE_ALLOW_UNVERIFIED_APPROVAL=1` is set; unverified approvals are marked
|
|
112
|
+
`verified: false`.
|
|
113
|
+
- **Persona supply-chain scan** (`scripts/ci/validate-assets.js`) — persona asset validation
|
|
114
|
+
now detects dangerous invisible unicode (zero-width, bidi overrides, Unicode tags) across
|
|
115
|
+
`.mindforge/personas` to prevent ASCII-smuggling injection.
|
|
116
|
+
- **Destructive-command detector** (`bin/security/trust-boundaries.js`) — `normalizeShell`
|
|
117
|
+
now strips bare `#` tokens after quote-stripping, blocking quoted-hash evasion (e.g.
|
|
118
|
+
`rm "#" -rf /`).
|
|
119
|
+
- **Instinct import port allowlist** (`bin/learning/lib/ssrf-guard.js`) — `validateImportUrl`
|
|
120
|
+
enforces an `ALLOWED_IMPORT_PORTS` allowlist (`{'', '443'}`), blocking attempts to reach
|
|
121
|
+
internal services (Redis `:6379`, Mongo `:27017`, etc.) on otherwise-allowed public hosts.
|
|
122
|
+
|
|
3
123
|
## [11.4.0] - 2026-06-06 — Claude Code plugin distribution
|
|
4
124
|
|
|
5
125
|
MindForge is now installable as a native **Claude Code plugin** from a marketplace, in
|
package/MINDFORGE.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
# MINDFORGE.md — Parameter Registry (v11.
|
|
1
|
+
# MINDFORGE.md — Parameter Registry (v11.5.1)
|
|
2
2
|
|
|
3
3
|
## 1. IDENTITY & VERSIONING
|
|
4
4
|
|
|
5
5
|
[NAME] = MindForge
|
|
6
|
-
[VERSION] = 11.
|
|
6
|
+
[VERSION] = 11.5.1
|
|
7
7
|
[STABLE] = true
|
|
8
8
|
[MODE] = "Platform Sovereign"
|
|
9
|
-
[REQUIRED_CORE_VERSION] = 11.
|
|
9
|
+
[REQUIRED_CORE_VERSION] = 11.5.1
|
|
10
10
|
[SOVEREIGN_IDENTITY] = true
|
|
11
11
|
[SRE_LAYER_ENABLED] = true
|
|
12
12
|
|
package/README.md
CHANGED
package/RELEASENOTES.md
CHANGED
|
@@ -1,5 +1,136 @@
|
|
|
1
1
|
# Release Notes
|
|
2
2
|
|
|
3
|
+
## v11.5.1 — Robustness + governance-gate patch
|
|
4
|
+
|
|
5
|
+
**Release Date**: 2026-06-11
|
|
6
|
+
**Type**: Patch (no API changes; one CI-gate behavior change)
|
|
7
|
+
**Upgrade Path**: `npx mindforge-cc@latest`
|
|
8
|
+
|
|
9
|
+
A fast-follow patch driven by a fresh adversarial audit of the shipped v11.5.0
|
|
10
|
+
tree. It hardens crash-prone JSON parsing in the autonomous/memory pipelines,
|
|
11
|
+
closes a governance-gate gap left by the v11.5.0 approval work, and tightens two
|
|
12
|
+
security surfaces. No features, no API changes.
|
|
13
|
+
|
|
14
|
+
### Robustness — no more crashes on a torn JSONL line
|
|
15
|
+
|
|
16
|
+
Three pipelines parsed JSON without guards, so one malformed/partially-written
|
|
17
|
+
line could crash them:
|
|
18
|
+
|
|
19
|
+
- `summarizePhase()` (pillar-health) parsed every `AUDIT.jsonl` line unguarded —
|
|
20
|
+
it now skips bad lines and keeps the valid ones.
|
|
21
|
+
- `captureFromCompaction()` (knowledge-capture) now returns `[]` on a malformed
|
|
22
|
+
`handoff.json` instead of throwing.
|
|
23
|
+
- `federated-sync` now tolerates a corrupted `sync-stats.json` in both
|
|
24
|
+
`handleSyncFailure` and `resetFailures` (falls back to `{failures:0}`).
|
|
25
|
+
|
|
26
|
+
### Security & governance
|
|
27
|
+
|
|
28
|
+
- **The CI Tier-3 gate now actually validates approvals.** Previously it only
|
|
29
|
+
counted approval files; a hand-committed empty file would pass. It now requires
|
|
30
|
+
each approval to carry a signature and be EITHER GPG-verified
|
|
31
|
+
(`verified: true`) OR an explicitly opted-in unverified approval
|
|
32
|
+
(`unverified_ack`, minted by `approve.js` under
|
|
33
|
+
`MINDFORGE_ALLOW_UNVERIFIED_APPROVAL=1` for repos without GPG infra). Bare or
|
|
34
|
+
stale `verified:false` files are still rejected — completing the v11.5.0
|
|
35
|
+
fail-closed `approve.js` work.
|
|
36
|
+
- **Dashboard approvals can't forge an identity.** `POST /api/approve/:id` no
|
|
37
|
+
longer records a client-supplied `approver` into the audit trail; it attributes
|
|
38
|
+
the action to a fixed authenticated actor. (The dashboard is already
|
|
39
|
+
localhost-bound and Bearer-token gated, so this is attribution hardening.)
|
|
40
|
+
- **The destructive-command guard now blocks Unix `truncate -s`.** In-place file
|
|
41
|
+
zeroing (`truncate -s 0 <path>`) was missed by the SQL-only pattern; it is now
|
|
42
|
+
gated, with benign uses unaffected.
|
|
43
|
+
- **A shipped module that couldn't load is fixed.** `bin/review/ads-engine.js`
|
|
44
|
+
required the uninstalled `uuid` package — so it (and the `federated-sync` that
|
|
45
|
+
imports it) threw on load in a clean install. Swapped to the built-in
|
|
46
|
+
`crypto.randomUUID()`; no new dependency.
|
|
47
|
+
|
|
48
|
+
## v11.5.0 — Governance hardening + autonomous-engine repair
|
|
49
|
+
|
|
50
|
+
**Release Date**: 2026-06-11
|
|
51
|
+
**Type**: Minor (one behavior change — see "Heads-up" below)
|
|
52
|
+
**Upgrade Path**: `npx mindforge-cc@latest`
|
|
53
|
+
|
|
54
|
+
This release consolidates four waves of work into a single minor: new orchestration and
|
|
55
|
+
learning primitives, a broad governance/security hardening pass, and — most importantly —
|
|
56
|
+
a repair that takes the autonomous engine from "crashes on every wave" to actually
|
|
57
|
+
functional. Some new pieces ship deliberately **inert** (present but not wired in); those
|
|
58
|
+
are called out so you know not to expect new behavior from them yet.
|
|
59
|
+
|
|
60
|
+
### Heads-up — Tier-3 approvals now fail closed (the one behavior change)
|
|
61
|
+
|
|
62
|
+
`bin/governance/approve.js` no longer rubber-stamps approvals. Tier-3 approvals now
|
|
63
|
+
**require GPG verification** and will **fail before writing any record** if no GPG key is
|
|
64
|
+
configured. If you relied on the old, unverified path, you have two choices:
|
|
65
|
+
|
|
66
|
+
- Configure a GPG key (recommended), or
|
|
67
|
+
- Set `MINDFORGE_ALLOW_UNVERIFIED_APPROVAL=1` to keep the old behavior — in which case the
|
|
68
|
+
approval record is written with `verified: false` so the gap is auditable.
|
|
69
|
+
|
|
70
|
+
This is the only item in v11.5.0 that can change an existing workflow's outcome.
|
|
71
|
+
|
|
72
|
+
### The autonomous engine is functional again
|
|
73
|
+
|
|
74
|
+
`/mindforge:auto` was effectively broken. Three fixes in `bin/autonomous/auto-runner.js`
|
|
75
|
+
bring it back:
|
|
76
|
+
|
|
77
|
+
- **No more per-wave crash.** The runner was calling a non-existent `getIdentity()` on the
|
|
78
|
+
ZTAI singleton and dying on every wave. It now establishes identity through the real
|
|
79
|
+
`registerAgent` API (`_getRunnerIdentity()`).
|
|
80
|
+
- **The policy gate actually enforces now.** The async policy verdict was never `await`ed,
|
|
81
|
+
so the gate silently allowed everything. It now awaits `policyEngine.evaluate(intent)` on
|
|
82
|
+
every wave.
|
|
83
|
+
- **It fails closed.** If runner identity can't be established, the gate now **denies and
|
|
84
|
+
audits** (`auto_mode_denied`) rather than running ungoverned.
|
|
85
|
+
|
|
86
|
+
### Security & governance hardening
|
|
87
|
+
|
|
88
|
+
- **SSRF guard hardened (twice).** The import-URL guard now closes an IPv6 link-local
|
|
89
|
+
bypass and a symlink path-traversal bypass via numeric bitmask + canonical-path checks.
|
|
90
|
+
Separately, remote instinct imports now enforce a port allowlist (`443`/none only), so an
|
|
91
|
+
attacker can no longer pivot through an allowed public host to reach internal services
|
|
92
|
+
like Redis (`:6379`) or Mongo (`:27017`).
|
|
93
|
+
- **Persona supply-chain scan.** Persona asset validation now flags dangerous invisible
|
|
94
|
+
unicode (zero-width, bidi overrides, Unicode tags) across `.mindforge/personas`, closing
|
|
95
|
+
an ASCII-smuggling injection vector.
|
|
96
|
+
- **Destructive-command detector closed an evasion.** Quoted-hash tricks like
|
|
97
|
+
`rm "#" -rf /` are now caught.
|
|
98
|
+
- **Federated memory sync works again.** `eis-client`'s `getAuthHeader` was throwing on
|
|
99
|
+
every call; it now correctly registers a node identity and signs requests, so sync to
|
|
100
|
+
non-localhost EIS endpoints functions.
|
|
101
|
+
- **RBAC fails safe.** Tier elevation no longer throws for unregistered agents and resolves
|
|
102
|
+
tiers through the correct ZTAI API.
|
|
103
|
+
- **Fail-closed contracts are now tested.** New `trust-verifier` and `rbac-manager` test
|
|
104
|
+
suites lock in identity-verification and tier-authorization behavior so future governance
|
|
105
|
+
changes can't quietly regress them.
|
|
106
|
+
|
|
107
|
+
### Learning & cost routing
|
|
108
|
+
|
|
109
|
+
- **New instinct CLI** — `/mindforge:instinct` manages the JSONL instinct store
|
|
110
|
+
deterministically (no LLM spawn): `list`, `export`, `import`, `promote-candidates`, and
|
|
111
|
+
`prune`.
|
|
112
|
+
- **Instinct store schema** gains `project_id` (stable scoping) and a `source` field
|
|
113
|
+
(`auto-capture`/`manual`/`imported`/`observer`), with origin-weighted confidence —
|
|
114
|
+
auto-captured instincts start at `0.3`, manually added ones at `0.7`.
|
|
115
|
+
- **Cost-routing shadow mode is now real.** Arbitrage steering respects
|
|
116
|
+
`cost_routing.shadow_mode` (default on); in observe-only mode, selections are returned as
|
|
117
|
+
`authoritative: false` and logged as SHADOW, so you can watch the router's
|
|
118
|
+
recommendations without it taking the wheel.
|
|
119
|
+
|
|
120
|
+
### Shipped inert (no behavior change yet)
|
|
121
|
+
|
|
122
|
+
These landed as scaffolding and are **not** wired into any live path — they do nothing
|
|
123
|
+
until a follow-up enables them:
|
|
124
|
+
|
|
125
|
+
- **Manifest-driven install resolver** (`install-manifests.js`) — profile-to-module
|
|
126
|
+
expansion and dependency detection are implemented, but the adapter into the installer's
|
|
127
|
+
live `install()` path is deferred.
|
|
128
|
+
- **GAN-style harness personas** (`gan-evaluator`, `gan-generator`, `gan-planner`) — fully
|
|
129
|
+
scoped and documented, not yet attached to any command or workflow.
|
|
130
|
+
- **Typed Inter-Agent Message Protocol** (`handoff-schema.js`) — an internal orchestration
|
|
131
|
+
primitive (five message kinds, four priority levels, validation) for upcoming
|
|
132
|
+
agent-handoff work.
|
|
133
|
+
|
|
3
134
|
## v11.3.1 — Packaging hotfix
|
|
4
135
|
|
|
5
136
|
**Release Date**: 2026-06-05
|
package/SECURITY.md
CHANGED
|
@@ -108,6 +108,22 @@ Before submitting code that touches security-sensitive paths:
|
|
|
108
108
|
|
|
109
109
|
---
|
|
110
110
|
|
|
111
|
+
## Agentic-Harness Threat Model
|
|
112
|
+
|
|
113
|
+
This document covers application/code vulnerabilities. The **outward** harness threat
|
|
114
|
+
model — prompt injection, poisoned project config / hooks / MCP, supply-chain risk in
|
|
115
|
+
skills/agents, the lethal trifecta, sandboxing, and the autonomous-agent minimum-bar
|
|
116
|
+
checklist — lives in **[MINDFORGE-AGENTIC-SECURITY.md](./MINDFORGE-AGENTIC-SECURITY.md)**.
|
|
117
|
+
Both are required reading before running MindForge autonomously.
|
|
118
|
+
|
|
119
|
+
Minimum bar (see that doc for detail): separate agent identities · short-lived scoped
|
|
120
|
+
creds · sandbox untrusted work · deny egress by default · `permissions.deny` on
|
|
121
|
+
secret-bearing paths · sanitize foreign content · human approval for shell/egress/deploy
|
|
122
|
+
(TrustGate + Tier-3) · log tool calls (AUDIT.jsonl) · process-group kill + heartbeat ·
|
|
123
|
+
narrow disposable memory · scan skills/hooks/MCP/agents as supply-chain artifacts.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
111
127
|
## Known Mitigations & Limitations
|
|
112
128
|
|
|
113
129
|
- **ZK-proofs are simulated** — The Dilithium-5 / ZK-proof layer uses cryptographic simulation, not hardware-backed TEEs. It provides logical governance enforcement, not hardware-grade isolation.
|
|
@@ -687,12 +687,53 @@ class AutoRunner {
|
|
|
687
687
|
fs.writeFileSync(this.statePath, JSON.stringify(state, null, 2));
|
|
688
688
|
}
|
|
689
689
|
|
|
690
|
+
/**
|
|
691
|
+
* Lazily registers (once) the autonomous runner's own ZTAI identity and
|
|
692
|
+
* returns { did, tier }. ztai-manager is a SINGLETON (not a constructor) and
|
|
693
|
+
* exposes no getIdentity() — the runner must register a DID to obtain one.
|
|
694
|
+
* Cached on the instance so every wave evaluates under one stable identity.
|
|
695
|
+
* Tier 3: autonomous phase processing is a high-trust operation; the policy
|
|
696
|
+
* engine still runs its own blast-radius analysis on top, so this is an INPUT
|
|
697
|
+
* to evaluation, not a self-granted bypass.
|
|
698
|
+
*/
|
|
699
|
+
async _getRunnerIdentity() {
|
|
700
|
+
if (!this._runnerIdentity) {
|
|
701
|
+
_ZTAIManager = lazyRequire(_ZTAIManager, '../governance/ztai-manager');
|
|
702
|
+
const did = await _ZTAIManager.registerAgent(
|
|
703
|
+
`auto-runner:${process.env.MF_PROJECT_ID || 'MF-ALPHA'}:phase-${this.phase}`,
|
|
704
|
+
3,
|
|
705
|
+
this._sessionId
|
|
706
|
+
);
|
|
707
|
+
const agent = _ZTAIManager.getAgent(did);
|
|
708
|
+
this._runnerIdentity = { did, tier: agent && typeof agent.tier === 'number' ? agent.tier : 3 };
|
|
709
|
+
}
|
|
710
|
+
return this._runnerIdentity;
|
|
711
|
+
}
|
|
712
|
+
|
|
690
713
|
async evaluateWavePolicy() {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
714
|
+
let identity;
|
|
715
|
+
try {
|
|
716
|
+
identity = await this._getRunnerIdentity();
|
|
717
|
+
} catch (err) {
|
|
718
|
+
// Fail CLOSED: if the runner cannot establish a verifiable identity, deny
|
|
719
|
+
// the wave rather than proceeding ungoverned.
|
|
720
|
+
console.warn(`[APO-DENY] Could not establish runner identity: ${err.message}`);
|
|
721
|
+
this.writeAudit({ event: 'auto_mode_denied', reason: `identity unavailable: ${err.message}`, phase: this.phase });
|
|
722
|
+
return false;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const intent = {
|
|
726
|
+
did: identity.did,
|
|
727
|
+
action: 'process_phase_wave',
|
|
728
|
+
resource: `projects/${process.env.MF_PROJECT_ID || 'MF-ALPHA'}/phases/${this.phase}/*`,
|
|
729
|
+
tier: identity.tier,
|
|
730
|
+
sessionId: this._sessionId,
|
|
731
|
+
metadata: { engine: 'Nimbus-S4', mode: 'autonomous', wave_timestamp: new Date().toISOString() }
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
// policyEngine.evaluate is ASYNC — must be awaited, or `result` is a Promise
|
|
735
|
+
// and `result.verdict === 'DENY'` is always false (the gate never fires).
|
|
736
|
+
const result = await this.policyEngine.evaluate(intent);
|
|
696
737
|
if (result.verdict === 'DENY') { console.warn(`[APO-DENY] ${result.reason}`); return false; }
|
|
697
738
|
return true;
|
|
698
739
|
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MindForge — Typed inter-agent message protocol.
|
|
5
|
+
*
|
|
6
|
+
* Ports ECC's ecc2/src/comms/mod.rs MessageType + TaskPriority as a JSON-schema
|
|
7
|
+
* validator. Complements state-manager.js validateHandoff (which validates the
|
|
8
|
+
* HANDOFF.json envelope) by typing the individual messages agents exchange:
|
|
9
|
+
*
|
|
10
|
+
* kinds: task_handoff | query | response | completed | conflict
|
|
11
|
+
* priority: low | normal | high | critical (default normal; legacy fallback)
|
|
12
|
+
*
|
|
13
|
+
* The Conflict kind pairs with the worktree engine's merge-readiness output
|
|
14
|
+
* (bin/worktree/engine.js). This is discipline/typing, not new runtime behavior.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const MESSAGE_KINDS = ['task_handoff', 'query', 'response', 'completed', 'conflict'];
|
|
18
|
+
const PRIORITIES = ['low', 'normal', 'high', 'critical'];
|
|
19
|
+
const DEFAULT_PRIORITY = 'normal';
|
|
20
|
+
|
|
21
|
+
// Required fields per kind (mirrors ECC's MessageType variants).
|
|
22
|
+
const REQUIRED_FIELDS = {
|
|
23
|
+
task_handoff: ['task', 'context'],
|
|
24
|
+
query: ['question'],
|
|
25
|
+
response: ['answer'],
|
|
26
|
+
completed: ['summary'], // files_changed optional, defaults []
|
|
27
|
+
conflict: ['file', 'description'],
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Validate a typed message object. Returns { valid, warnings }.
|
|
32
|
+
* Fail-open style (matches validateHandoff): collects warnings, never throws.
|
|
33
|
+
*/
|
|
34
|
+
function validateMessage(msg) {
|
|
35
|
+
const warnings = [];
|
|
36
|
+
if (!msg || typeof msg !== 'object' || Array.isArray(msg)) {
|
|
37
|
+
return { valid: false, warnings: ['message is not an object'] };
|
|
38
|
+
}
|
|
39
|
+
if (!MESSAGE_KINDS.includes(msg.kind)) {
|
|
40
|
+
warnings.push(`invalid kind: "${msg.kind}". Expected one of: ${MESSAGE_KINDS.join(', ')}`);
|
|
41
|
+
return { valid: false, warnings };
|
|
42
|
+
}
|
|
43
|
+
for (const field of REQUIRED_FIELDS[msg.kind]) {
|
|
44
|
+
if (typeof msg[field] !== 'string' || msg[field].length === 0) {
|
|
45
|
+
warnings.push(`${msg.kind} missing required string field: ${field}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (msg.kind === 'completed' && msg.files_changed !== undefined && !Array.isArray(msg.files_changed)) {
|
|
49
|
+
warnings.push('completed.files_changed must be an array');
|
|
50
|
+
}
|
|
51
|
+
if (msg.kind === 'task_handoff' && msg.priority !== undefined && !PRIORITIES.includes(msg.priority)) {
|
|
52
|
+
warnings.push(`invalid priority: "${msg.priority}". Expected one of: ${PRIORITIES.join(', ')}`);
|
|
53
|
+
}
|
|
54
|
+
return { valid: warnings.length === 0, warnings };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Resolve a handoff message's priority, defaulting to "normal" and tolerating
|
|
59
|
+
* legacy entries that lack a typed priority (ECC's legacy fallback).
|
|
60
|
+
*/
|
|
61
|
+
function handoffPriority(msg) {
|
|
62
|
+
if (!msg || typeof msg !== 'object') return DEFAULT_PRIORITY;
|
|
63
|
+
const p = msg.priority;
|
|
64
|
+
return PRIORITIES.includes(p) ? p : DEFAULT_PRIORITY;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* One-line human preview of a typed message (for status/log surfaces).
|
|
69
|
+
*/
|
|
70
|
+
function preview(msg) {
|
|
71
|
+
const trunc = (s, n) => {
|
|
72
|
+
const t = String(s || '').trim();
|
|
73
|
+
return t.length <= n ? t : `${t.slice(0, n - 1)}…`;
|
|
74
|
+
};
|
|
75
|
+
switch (msg && msg.kind) {
|
|
76
|
+
case 'task_handoff': {
|
|
77
|
+
const p = handoffPriority(msg);
|
|
78
|
+
return p === DEFAULT_PRIORITY
|
|
79
|
+
? `handoff ${trunc(msg.task, 56)}`
|
|
80
|
+
: `handoff [${p}] ${trunc(msg.task, 48)}`;
|
|
81
|
+
}
|
|
82
|
+
case 'query': return `query ${trunc(msg.question, 56)}`;
|
|
83
|
+
case 'response': return `response ${trunc(msg.answer, 56)}`;
|
|
84
|
+
case 'completed': {
|
|
85
|
+
const n = Array.isArray(msg.files_changed) ? msg.files_changed.length : 0;
|
|
86
|
+
return n === 0 ? `completed ${trunc(msg.summary, 48)}` : `completed ${trunc(msg.summary, 40)} | ${n} files`;
|
|
87
|
+
}
|
|
88
|
+
case 'conflict': return `conflict ${msg.file} | ${trunc(msg.description, 40)}`;
|
|
89
|
+
default: return `unknown ${trunc(JSON.stringify(msg), 56)}`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Build a well-formed message of a given kind (convenience + normalization).
|
|
95
|
+
* Throws if the result fails validation, so callers can't emit a bad message.
|
|
96
|
+
*/
|
|
97
|
+
function makeMessage(kind, fields = {}) {
|
|
98
|
+
const msg = Object.assign({ kind }, fields);
|
|
99
|
+
if (kind === 'task_handoff' && msg.priority === undefined) msg.priority = DEFAULT_PRIORITY;
|
|
100
|
+
if (kind === 'completed' && msg.files_changed === undefined) msg.files_changed = [];
|
|
101
|
+
const { valid, warnings } = validateMessage(msg);
|
|
102
|
+
if (!valid) throw new Error(`invalid ${kind} message: ${warnings.join('; ')}`);
|
|
103
|
+
return msg;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = {
|
|
107
|
+
MESSAGE_KINDS,
|
|
108
|
+
PRIORITIES,
|
|
109
|
+
DEFAULT_PRIORITY,
|
|
110
|
+
validateMessage,
|
|
111
|
+
handoffPriority,
|
|
112
|
+
preview,
|
|
113
|
+
makeMessage,
|
|
114
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# session-guardian.sh — Autonomous-loop session guard (MindForge)
|
|
3
|
+
# Exit 0 = proceed. Exit 1 = skip this loop cycle.
|
|
4
|
+
# Called by any bin/autonomous loop (and the deferred background observer)
|
|
5
|
+
# BEFORE spawning a model session, so the loop cannot burn tokens overnight,
|
|
6
|
+
# AFK, or faster than its cooldown.
|
|
7
|
+
#
|
|
8
|
+
# Ported near-verbatim from ECC (continuous-learning-v2/agents/session-guardian.sh).
|
|
9
|
+
# Env defaults map to a new instincts.observer config block (default-off posture):
|
|
10
|
+
# OBSERVER_INTERVAL_SECONDS default: 300 (per-project cooldown)
|
|
11
|
+
# OBSERVER_LAST_RUN_LOG default: ~/.mindforge/observer-last-run.log
|
|
12
|
+
# OBSERVER_ACTIVE_HOURS_START default: 800 (8:00 AM local, 0 to disable)
|
|
13
|
+
# OBSERVER_ACTIVE_HOURS_END default: 2300 (11:00 PM local, 0 to disable)
|
|
14
|
+
# OBSERVER_MAX_IDLE_SECONDS default: 1800 (30 min; 0 to disable)
|
|
15
|
+
#
|
|
16
|
+
# Gate order (cheapest first):
|
|
17
|
+
# Gate 1: Time window check (~0ms)
|
|
18
|
+
# Gate 2: Project cooldown log (~1ms, mkdir lock)
|
|
19
|
+
# Gate 3: Idle detection (~5-50ms, OS syscall; fail open)
|
|
20
|
+
|
|
21
|
+
set -euo pipefail
|
|
22
|
+
|
|
23
|
+
INTERVAL="${OBSERVER_INTERVAL_SECONDS:-300}"
|
|
24
|
+
LOG_PATH="${OBSERVER_LAST_RUN_LOG:-$HOME/.mindforge/observer-last-run.log}"
|
|
25
|
+
ACTIVE_START="${OBSERVER_ACTIVE_HOURS_START:-800}"
|
|
26
|
+
ACTIVE_END="${OBSERVER_ACTIVE_HOURS_END:-2300}"
|
|
27
|
+
MAX_IDLE="${OBSERVER_MAX_IDLE_SECONDS:-1800}"
|
|
28
|
+
|
|
29
|
+
# ── Gate 1: Time Window ───────────────────────────────────────────────────────
|
|
30
|
+
if [ "$ACTIVE_START" -ne 0 ] || [ "$ACTIVE_END" -ne 0 ]; then
|
|
31
|
+
current_hhmm=$(date +%k%M | tr -d ' ')
|
|
32
|
+
current_hhmm_num=$(( 10#${current_hhmm:-0} ))
|
|
33
|
+
active_start_num=$(( 10#${ACTIVE_START:-800} ))
|
|
34
|
+
active_end_num=$(( 10#${ACTIVE_END:-2300} ))
|
|
35
|
+
|
|
36
|
+
within_active_hours=0
|
|
37
|
+
if [ "$active_start_num" -lt "$active_end_num" ]; then
|
|
38
|
+
if [ "$current_hhmm_num" -ge "$active_start_num" ] && [ "$current_hhmm_num" -lt "$active_end_num" ]; then
|
|
39
|
+
within_active_hours=1
|
|
40
|
+
fi
|
|
41
|
+
else
|
|
42
|
+
if [ "$current_hhmm_num" -ge "$active_start_num" ] || [ "$current_hhmm_num" -lt "$active_end_num" ]; then
|
|
43
|
+
within_active_hours=1
|
|
44
|
+
fi
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
if [ "$within_active_hours" -ne 1 ]; then
|
|
48
|
+
echo "session-guardian: outside active hours (${current_hhmm}, window ${ACTIVE_START}-${ACTIVE_END})" >&2
|
|
49
|
+
exit 1
|
|
50
|
+
fi
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# ── Gate 2: Project Cooldown Log ─────────────────────────────────────────────
|
|
54
|
+
project_root="${PROJECT_DIR:-}"
|
|
55
|
+
if [ -z "$project_root" ] || [ ! -d "$project_root" ]; then
|
|
56
|
+
project_root="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")"
|
|
57
|
+
fi
|
|
58
|
+
project_name="$(basename "$project_root")"
|
|
59
|
+
now="$(date +%s)"
|
|
60
|
+
|
|
61
|
+
mkdir -p "$(dirname "$LOG_PATH")" || {
|
|
62
|
+
echo "session-guardian: cannot create log dir, proceeding" >&2
|
|
63
|
+
exit 0
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
_lock_dir="${LOG_PATH}.lock"
|
|
67
|
+
if ! mkdir "$_lock_dir" 2>/dev/null; then
|
|
68
|
+
echo "session-guardian: log locked by concurrent process, skipping cycle" >&2
|
|
69
|
+
exit 1
|
|
70
|
+
else
|
|
71
|
+
trap 'rm -rf "$_lock_dir"' EXIT INT TERM
|
|
72
|
+
|
|
73
|
+
last_spawn=0
|
|
74
|
+
last_spawn=$(awk -F '\t' -v key="$project_root" '$1 == key { value = $2 } END { if (value != "") print value }' "$LOG_PATH" 2>/dev/null) || true
|
|
75
|
+
last_spawn="${last_spawn:-0}"
|
|
76
|
+
[[ "$last_spawn" =~ ^[0-9]+$ ]] || last_spawn=0
|
|
77
|
+
|
|
78
|
+
elapsed=$(( now - last_spawn ))
|
|
79
|
+
if [ "$elapsed" -lt "$INTERVAL" ]; then
|
|
80
|
+
rm -rf "$_lock_dir"
|
|
81
|
+
trap - EXIT INT TERM
|
|
82
|
+
echo "session-guardian: cooldown active for '${project_name}' (last spawn ${elapsed}s ago, interval ${INTERVAL}s)" >&2
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
tmp_log="$(mktemp "$(dirname "$LOG_PATH")/observer-last-run.XXXXXX")"
|
|
87
|
+
awk -F '\t' -v key="$project_root" '$1 != key' "$LOG_PATH" > "$tmp_log" 2>/dev/null || true
|
|
88
|
+
printf '%s\t%s\n' "$project_root" "$now" >> "$tmp_log"
|
|
89
|
+
mv "$tmp_log" "$LOG_PATH"
|
|
90
|
+
|
|
91
|
+
rm -rf "$_lock_dir"
|
|
92
|
+
trap - EXIT INT TERM
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# ── Gate 3: Idle Detection ────────────────────────────────────────────────────
|
|
96
|
+
get_idle_seconds() {
|
|
97
|
+
local _raw
|
|
98
|
+
case "$(uname -s)" in
|
|
99
|
+
Darwin)
|
|
100
|
+
_raw=$( { /usr/sbin/ioreg -c IOHIDSystem \
|
|
101
|
+
| /usr/bin/awk '/HIDIdleTime/ {print int($NF/1000000000); exit}'; } \
|
|
102
|
+
2>/dev/null ) || true
|
|
103
|
+
printf '%s\n' "${_raw:-0}" | head -n1
|
|
104
|
+
;;
|
|
105
|
+
Linux)
|
|
106
|
+
if command -v xprintidle >/dev/null 2>&1; then
|
|
107
|
+
_raw=$(xprintidle 2>/dev/null) || true
|
|
108
|
+
echo $(( ${_raw:-0} / 1000 ))
|
|
109
|
+
else
|
|
110
|
+
echo 0 # fail open: xprintidle not installed
|
|
111
|
+
fi
|
|
112
|
+
;;
|
|
113
|
+
*MINGW*|*MSYS*|*CYGWIN*)
|
|
114
|
+
_raw=$(powershell.exe -NoProfile -NonInteractive -Command \
|
|
115
|
+
"try { \
|
|
116
|
+
Add-Type -MemberDefinition '[DllImport(\"user32.dll\")] public static extern bool GetLastInputInfo(ref LASTINPUTINFO p); [StructLayout(LayoutKind.Sequential)] public struct LASTINPUTINFO { public uint cbSize; public int dwTime; }' -Name WinAPI -Namespace PInvoke; \
|
|
117
|
+
\$l = New-Object PInvoke.WinAPI+LASTINPUTINFO; \$l.cbSize = 8; \
|
|
118
|
+
[PInvoke.WinAPI]::GetLastInputInfo([ref]\$l) | Out-Null; \
|
|
119
|
+
[int][Math]::Max(0, [long]([Environment]::TickCount - [long]\$l.dwTime) / 1000) \
|
|
120
|
+
} catch { 0 }" \
|
|
121
|
+
2>/dev/null | tr -d '\r') || true
|
|
122
|
+
printf '%s\n' "${_raw:-0}" | head -n1
|
|
123
|
+
;;
|
|
124
|
+
*)
|
|
125
|
+
echo 0 # fail open: unknown platform
|
|
126
|
+
;;
|
|
127
|
+
esac
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if [ "$MAX_IDLE" -gt 0 ]; then
|
|
131
|
+
idle_seconds=$(get_idle_seconds)
|
|
132
|
+
if [ "$idle_seconds" -gt "$MAX_IDLE" ]; then
|
|
133
|
+
echo "session-guardian: user idle ${idle_seconds}s (threshold ${MAX_IDLE}s), skipping" >&2
|
|
134
|
+
exit 1
|
|
135
|
+
fi
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
exit 0
|