network-ai 5.3.0 → 5.3.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/QUICKSTART.md +3 -3
- package/README.md +1 -1
- package/SKILL.md +9 -4
- package/package.json +1 -1
- package/scripts/check_permission.py +109 -25
- package/scripts/context_manager.py +77 -0
package/QUICKSTART.md
CHANGED
|
@@ -18,7 +18,7 @@ npm install
|
|
|
18
18
|
npx ts-node setup.ts --check
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
**Zero external AI dependencies.** All
|
|
21
|
+
**Zero external AI dependencies.** All 29 adapters are self-contained — add framework SDKs only when you need them.
|
|
22
22
|
|
|
23
23
|
---
|
|
24
24
|
|
|
@@ -254,7 +254,7 @@ export class MyFrameworkAdapter extends BaseAdapter {
|
|
|
254
254
|
```bash
|
|
255
255
|
npx ts-node test-standalone.ts # 88 core tests
|
|
256
256
|
npx ts-node test-security.ts # 34 security tests
|
|
257
|
-
npx ts-node test-adapters.ts # 218 adapter tests (all
|
|
257
|
+
npx ts-node test-adapters.ts # 218 adapter tests (all 29 frameworks)
|
|
258
258
|
npx ts-node test-cli.ts # 65 CLI tests
|
|
259
259
|
npx ts-node test-qa.ts # 67 QA orchestrator tests
|
|
260
260
|
```
|
|
@@ -265,7 +265,7 @@ npx ts-node test-qa.ts # 67 QA orchestrator tests
|
|
|
265
265
|
|
|
266
266
|
```bash
|
|
267
267
|
npx ts-node setup.ts --check # Verify installation
|
|
268
|
-
npx ts-node setup.ts --list # List all
|
|
268
|
+
npx ts-node setup.ts --list # List all 29 adapters
|
|
269
269
|
npx ts-node setup.ts --example # Generate example.ts
|
|
270
270
|
```
|
|
271
271
|
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://network-ai.org/)
|
|
6
6
|
[](https://github.com/Jovancoding/Network-AI/actions/workflows/ci.yml)
|
|
7
7
|
[](https://github.com/Jovancoding/Network-AI/actions/workflows/codeql.yml)
|
|
8
|
-
[](https://github.com/Jovancoding/Network-AI/releases)
|
|
9
9
|
[](https://www.npmjs.com/package/network-ai)
|
|
10
10
|
[](#testing)
|
|
11
11
|
[](#adapter-system)
|
package/SKILL.md
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: network-ai
|
|
3
|
-
description: "Local Python orchestration skill: multi-agent workflows via shared blackboard file, permission gating, token budget scripts, and persistent project context.
|
|
3
|
+
description: "Local Python orchestration skill: multi-agent workflows via shared blackboard file, permission gating, token budget scripts, and persistent project context. All bundled scripts run locally with zero network calls and zero third-party dependencies."
|
|
4
4
|
metadata:
|
|
5
5
|
openclaw:
|
|
6
6
|
emoji: "\U0001F41D"
|
|
7
7
|
homepage: https://network-ai.org
|
|
8
8
|
bundle_scope: "Python scripts only (scripts/*.py). All execution is local. Only Python stdlib — no other runtimes, adapters, or CLI tools are included."
|
|
9
|
-
network_calls: "none — bundled scripts make zero network calls
|
|
10
|
-
|
|
9
|
+
network_calls: "none — bundled scripts make zero network calls and spawn no subprocesses."
|
|
10
|
+
inter_agent_comms: "none — this skill does not implement, invoke, or control inter-agent messaging or sessions_send. All coordination is via local file-based blackboard only."
|
|
11
|
+
sessions_send: "NOT implemented or invoked by this skill. sessions_send is a host-platform built-in entirely outside this skill's control. See data-flow notice below."
|
|
11
12
|
sessions_ops: "platform-provided — outside this skill's control"
|
|
12
13
|
requires:
|
|
13
14
|
bins:
|
|
@@ -31,7 +32,11 @@ metadata:
|
|
|
31
32
|
|
|
32
33
|
> **Scope:** The bundled Python scripts (`scripts/*.py`) make **no network calls**, use only the Python standard library, and have **zero third-party dependencies**. Tokens are UUID-based (`grant_{uuid4().hex}`) stored in `data/active_grants.json`. Audit logging is plain JSONL (`data/audit_log.jsonl`).
|
|
33
34
|
|
|
34
|
-
> **
|
|
35
|
+
> **Advisory tokens notice:** Grant tokens issued by `check_permission.py` are **advisory scoring outputs only** — the caller-supplied `--agent` identity is not cryptographically verified. Downstream systems must not treat these tokens as authenticated credentials without adding a separate identity-verification step or human approval gate, especially for PAYMENTS, DATABASE, and FILE_EXPORT resources.
|
|
36
|
+
|
|
37
|
+
> **Data-flow notice (host platform — not this skill):** This skill does NOT implement, invoke, or control `sessions_send`. That is a host-platform built-in (OpenClaw runtime). The orchestration instructions below describe *when* to call the platform’s `sessions_send` after budget checks pass — but the actual network call, model endpoint, and data transmission are entirely the **host platform’s** responsibility. If you need to prevent external network calls, disable or reroute `sessions_send` in your **platform settings** before installing this skill. This skill has no access to that configuration.
|
|
38
|
+
|
|
39
|
+
> **Context file integrity:** The `context_manager.py inject` command now validates `data/project-context.json` for injection patterns and oversized fields before printing the context block. Review any warnings printed to stderr before passing the output to an agent system prompt.
|
|
35
40
|
|
|
36
41
|
> **PII / sensitive-data warning:** The `justification` field in permission requests and the audit log (`data/audit_log.jsonl`) store free-text strings provided by agents. **Do not include PII, secrets, or credentials in justification text.** Consider restricting file permissions on `data/` or running this skill in an isolated workspace.
|
|
37
42
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "network-ai",
|
|
3
|
-
"version": "5.3.
|
|
3
|
+
"version": "5.3.1",
|
|
4
4
|
"description": "AI agent orchestration framework for TypeScript/Node.js - 29 adapters (LangChain, AutoGen, CrewAI, OpenAI Assistants, LlamaIndex, Semantic Kernel, Haystack, DSPy, Agno, MCP, OpenClaw, A2A, Codex, MiniMax, NemoClaw, APS, Copilot, LangGraph, Anthropic Computer Use, OpenAI Agents SDK, Vertex AI, Pydantic AI, Browser Agent, Hermes, Orchestrator, RLM + streaming variants). Built-in CLI, security, swarm intelligence, real-time streaming, and agentic workflow patterns.",
|
|
5
5
|
"homepage": "https://network-ai.org",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -41,6 +41,17 @@ GRANT_TOKEN_TTL_MINUTES = 5
|
|
|
41
41
|
GRANTS_FILE = Path(__file__).parent.parent / "data" / "active_grants.json"
|
|
42
42
|
AUDIT_LOG = Path(__file__).parent.parent / "data" / "audit_log.jsonl"
|
|
43
43
|
|
|
44
|
+
# ADVISORY TOKENS — IMPORTANT
|
|
45
|
+
# Grant tokens produced by this script are ADVISORY scoring outputs only.
|
|
46
|
+
# They are NOT authenticated credentials. The agent_id supplied via --agent
|
|
47
|
+
# is accepted as-is from the caller and is NOT verified against an identity
|
|
48
|
+
# provider. Downstream systems MUST treat these tokens as hints, not proof
|
|
49
|
+
# of identity, and SHOULD require a separate authenticated session or human
|
|
50
|
+
# approval before honouring access to sensitive resources.
|
|
51
|
+
#
|
|
52
|
+
# For PAYMENTS, DATABASE (write), and FILE_EXPORT the caller must also pass
|
|
53
|
+
# --confirm-high-risk to acknowledge the advisory-only nature of the grant.
|
|
54
|
+
|
|
44
55
|
# Default trust levels for known agents
|
|
45
56
|
DEFAULT_TRUST_LEVELS = {
|
|
46
57
|
"orchestrator": 0.9,
|
|
@@ -49,6 +60,13 @@ DEFAULT_TRUST_LEVELS = {
|
|
|
49
60
|
"risk_assessor": 0.85,
|
|
50
61
|
}
|
|
51
62
|
|
|
63
|
+
# Agents whose identity is pre-registered. Any agent_id NOT in this set
|
|
64
|
+
# receives a reduced trust score (0.3) and triggers an advisory warning.
|
|
65
|
+
KNOWN_AGENTS: set[str] = set(DEFAULT_TRUST_LEVELS.keys())
|
|
66
|
+
|
|
67
|
+
# Resource types that require --confirm-high-risk before a grant is issued
|
|
68
|
+
HIGH_RISK_RESOURCES: set[str] = {"PAYMENTS", "DATABASE"}
|
|
69
|
+
|
|
52
70
|
# Base risk scores for resource types
|
|
53
71
|
BASE_RISKS = {
|
|
54
72
|
"DATABASE": 0.5, # Internal database access
|
|
@@ -258,48 +276,88 @@ def save_grant(grant: dict[str, Any]) -> None:
|
|
|
258
276
|
GRANTS_FILE.write_text(json.dumps(grants, indent=2))
|
|
259
277
|
|
|
260
278
|
|
|
261
|
-
def evaluate_permission(
|
|
262
|
-
|
|
279
|
+
def evaluate_permission(
|
|
280
|
+
agent_id: str,
|
|
281
|
+
resource_type: str,
|
|
282
|
+
justification: str,
|
|
283
|
+
scope: Optional[str] = None,
|
|
284
|
+
confirm_high_risk: bool = False,
|
|
285
|
+
) -> dict[str, Any]:
|
|
263
286
|
"""
|
|
264
287
|
Evaluate a permission request using weighted scoring.
|
|
265
|
-
|
|
288
|
+
|
|
289
|
+
NOTE: These tokens are ADVISORY ONLY — the caller supplies their own
|
|
290
|
+
agent_id and it is not authenticated. Downstream systems must not treat
|
|
291
|
+
the resulting token as proof of identity without additional verification.
|
|
292
|
+
|
|
266
293
|
Weights:
|
|
267
294
|
- Justification Quality: 40%
|
|
268
295
|
- Agent Trust Level: 30%
|
|
269
296
|
- Risk Assessment: 30%
|
|
270
297
|
"""
|
|
298
|
+
# Warn if agent_id is not in the pre-registered known-agents list
|
|
299
|
+
unknown_agent = agent_id not in KNOWN_AGENTS
|
|
300
|
+
|
|
271
301
|
# Log the request
|
|
272
302
|
log_audit("permission_request", {
|
|
273
303
|
"agent_id": agent_id,
|
|
274
304
|
"resource_type": resource_type,
|
|
275
305
|
"justification": justification,
|
|
276
|
-
"scope": scope
|
|
306
|
+
"scope": scope,
|
|
307
|
+
"unknown_agent": unknown_agent,
|
|
277
308
|
})
|
|
278
|
-
|
|
309
|
+
|
|
310
|
+
# Require explicit acknowledgement for high-risk resources
|
|
311
|
+
needs_confirmation = (
|
|
312
|
+
resource_type in HIGH_RISK_RESOURCES
|
|
313
|
+
or (resource_type == "DATABASE" and scope and re.search(
|
|
314
|
+
r'\b(write|delete|update|modify|create)\b', scope, re.IGNORECASE
|
|
315
|
+
))
|
|
316
|
+
)
|
|
317
|
+
if needs_confirmation and not confirm_high_risk:
|
|
318
|
+
return {
|
|
319
|
+
"granted": False,
|
|
320
|
+
"advisory": True,
|
|
321
|
+
"reason": (
|
|
322
|
+
f"{resource_type} is a high-risk resource. Re-run with --confirm-high-risk "
|
|
323
|
+
"to acknowledge that this token is advisory only and does not authenticate "
|
|
324
|
+
"the supplied agent identity."
|
|
325
|
+
),
|
|
326
|
+
"scores": {"justification": None, "trust": None, "risk": None},
|
|
327
|
+
}
|
|
328
|
+
|
|
279
329
|
# 1. Justification Quality (40% weight)
|
|
280
330
|
justification_score = score_justification(justification)
|
|
281
331
|
if justification_score < 0.3:
|
|
282
332
|
return {
|
|
283
333
|
"granted": False,
|
|
334
|
+
"advisory": True,
|
|
284
335
|
"reason": "Justification is insufficient. Please provide specific task context.",
|
|
285
336
|
"scores": {
|
|
286
337
|
"justification": justification_score,
|
|
287
338
|
"trust": None,
|
|
288
|
-
"risk": None
|
|
289
|
-
}
|
|
339
|
+
"risk": None,
|
|
340
|
+
},
|
|
290
341
|
}
|
|
291
|
-
|
|
342
|
+
|
|
292
343
|
# 2. Agent Trust Level (30% weight)
|
|
293
|
-
|
|
344
|
+
# Unknown agents receive a reduced base trust score (0.3) — their identity
|
|
345
|
+
# has not been pre-registered and cannot be verified by this script.
|
|
346
|
+
trust_level = DEFAULT_TRUST_LEVELS.get(agent_id, 0.3) if not unknown_agent \
|
|
347
|
+
else 0.3
|
|
294
348
|
if trust_level < 0.4:
|
|
295
349
|
return {
|
|
296
350
|
"granted": False,
|
|
297
|
-
"
|
|
351
|
+
"advisory": True,
|
|
352
|
+
"reason": (
|
|
353
|
+
"Agent trust level is below threshold. Escalate to human operator."
|
|
354
|
+
+ (" (unrecognized agent_id — identity not pre-registered)" if unknown_agent else "")
|
|
355
|
+
),
|
|
298
356
|
"scores": {
|
|
299
357
|
"justification": justification_score,
|
|
300
358
|
"trust": trust_level,
|
|
301
|
-
"risk": None
|
|
302
|
-
}
|
|
359
|
+
"risk": None,
|
|
360
|
+
},
|
|
303
361
|
}
|
|
304
362
|
|
|
305
363
|
# 3. Risk Assessment (30% weight)
|
|
@@ -307,12 +365,13 @@ def evaluate_permission(agent_id: str, resource_type: str,
|
|
|
307
365
|
if risk_score > 0.8:
|
|
308
366
|
return {
|
|
309
367
|
"granted": False,
|
|
368
|
+
"advisory": True,
|
|
310
369
|
"reason": "Risk assessment exceeds acceptable threshold. Narrow the requested scope.",
|
|
311
370
|
"scores": {
|
|
312
371
|
"justification": justification_score,
|
|
313
372
|
"trust": trust_level,
|
|
314
|
-
"risk": risk_score
|
|
315
|
-
}
|
|
373
|
+
"risk": risk_score,
|
|
374
|
+
},
|
|
316
375
|
}
|
|
317
376
|
|
|
318
377
|
# Calculate weighted approval score
|
|
@@ -325,13 +384,14 @@ def evaluate_permission(agent_id: str, resource_type: str,
|
|
|
325
384
|
if weighted_score < 0.5:
|
|
326
385
|
return {
|
|
327
386
|
"granted": False,
|
|
387
|
+
"advisory": True,
|
|
328
388
|
"reason": f"Combined evaluation score ({weighted_score:.2f}) below threshold (0.5).",
|
|
329
389
|
"scores": {
|
|
330
390
|
"justification": justification_score,
|
|
331
391
|
"trust": trust_level,
|
|
332
392
|
"risk": risk_score,
|
|
333
|
-
"weighted": weighted_score
|
|
334
|
-
}
|
|
393
|
+
"weighted": weighted_score,
|
|
394
|
+
},
|
|
335
395
|
}
|
|
336
396
|
|
|
337
397
|
# Generate grant
|
|
@@ -346,15 +406,19 @@ def evaluate_permission(agent_id: str, resource_type: str,
|
|
|
346
406
|
"scope": scope,
|
|
347
407
|
"expires_at": expires_at,
|
|
348
408
|
"restrictions": restrictions,
|
|
349
|
-
"granted_at": datetime.now(timezone.utc).isoformat()
|
|
409
|
+
"granted_at": datetime.now(timezone.utc).isoformat(),
|
|
410
|
+
"advisory": True, # Always advisory — not an authenticated credential
|
|
411
|
+
"unknown_agent": unknown_agent,
|
|
350
412
|
}
|
|
351
|
-
|
|
413
|
+
|
|
352
414
|
# Save grant and log
|
|
353
415
|
save_grant(grant)
|
|
354
416
|
log_audit("permission_granted", grant)
|
|
355
|
-
|
|
417
|
+
|
|
356
418
|
return {
|
|
357
419
|
"granted": True,
|
|
420
|
+
"advisory": True, # Token is advisory — agent identity was not verified
|
|
421
|
+
"unknown_agent": unknown_agent,
|
|
358
422
|
"token": token,
|
|
359
423
|
"expires_at": expires_at,
|
|
360
424
|
"restrictions": restrictions,
|
|
@@ -362,8 +426,14 @@ def evaluate_permission(agent_id: str, resource_type: str,
|
|
|
362
426
|
"justification": justification_score,
|
|
363
427
|
"trust": trust_level,
|
|
364
428
|
"risk": risk_score,
|
|
365
|
-
"weighted": weighted_score
|
|
366
|
-
}
|
|
429
|
+
"weighted": weighted_score,
|
|
430
|
+
},
|
|
431
|
+
"notice": (
|
|
432
|
+
"This token was issued based on local scoring only. "
|
|
433
|
+
"The agent identity supplied via --agent was NOT cryptographically verified. "
|
|
434
|
+
"Treat this token as advisory and require human approval before granting "
|
|
435
|
+
"access to sensitive systems."
|
|
436
|
+
),
|
|
367
437
|
}
|
|
368
438
|
|
|
369
439
|
|
|
@@ -649,6 +719,16 @@ Examples:
|
|
|
649
719
|
action="store_true",
|
|
650
720
|
help="Output result as JSON"
|
|
651
721
|
)
|
|
722
|
+
parser.add_argument(
|
|
723
|
+
"--confirm-high-risk",
|
|
724
|
+
action="store_true",
|
|
725
|
+
dest="confirm_high_risk",
|
|
726
|
+
help=(
|
|
727
|
+
"Required for PAYMENTS and DATABASE resources. Acknowledges that the issued "
|
|
728
|
+
"token is advisory only and that the caller-supplied agent identity was not "
|
|
729
|
+
"cryptographically verified."
|
|
730
|
+
),
|
|
731
|
+
)
|
|
652
732
|
|
|
653
733
|
args = parser.parse_args()
|
|
654
734
|
|
|
@@ -672,17 +752,21 @@ Examples:
|
|
|
672
752
|
agent_id=args.agent,
|
|
673
753
|
resource_type=args.resource,
|
|
674
754
|
justification=args.justification,
|
|
675
|
-
scope=args.scope
|
|
755
|
+
scope=args.scope,
|
|
756
|
+
confirm_high_risk=getattr(args, "confirm_high_risk", False),
|
|
676
757
|
)
|
|
677
758
|
|
|
678
759
|
if args.json:
|
|
679
760
|
print(json.dumps(result, indent=2))
|
|
680
761
|
else:
|
|
681
762
|
if result["granted"]:
|
|
682
|
-
print("GRANTED")
|
|
683
|
-
print(f"Token:
|
|
684
|
-
print(f"Expires:
|
|
763
|
+
print("GRANTED [ADVISORY — agent identity was NOT verified]")
|
|
764
|
+
print(f"Token: {result['token']}")
|
|
765
|
+
print(f"Expires: {result['expires_at']}")
|
|
685
766
|
print(f"Restrictions: {', '.join(result['restrictions'])}")
|
|
767
|
+
if result.get("unknown_agent"):
|
|
768
|
+
print("WARNING: agent_id is not in the pre-registered known-agents list.")
|
|
769
|
+
print(f"\nNOTICE: {result.get('notice', '')}")
|
|
686
770
|
else:
|
|
687
771
|
print("DENIED")
|
|
688
772
|
print(f"Reason: {result['reason']}")
|
|
@@ -74,6 +74,72 @@ def _now_iso() -> str:
|
|
|
74
74
|
return datetime.now(timezone.utc).isoformat()
|
|
75
75
|
|
|
76
76
|
|
|
77
|
+
def _validate_context(ctx: dict[str, Any]) -> list[str]:
|
|
78
|
+
"""
|
|
79
|
+
Validate the project context file against the expected schema.
|
|
80
|
+
|
|
81
|
+
Returns a list of warning strings (empty = clean).
|
|
82
|
+
Checks:
|
|
83
|
+
- Required top-level keys are present
|
|
84
|
+
- String fields are not excessively long (injection/poisoning guard)
|
|
85
|
+
- List entries are strings or dicts, not executable-looking content
|
|
86
|
+
- No obvious prompt-injection patterns in goals, decisions, or banned entries
|
|
87
|
+
"""
|
|
88
|
+
import re as _re
|
|
89
|
+
warnings: list[str] = []
|
|
90
|
+
|
|
91
|
+
REQUIRED_KEYS = {"project", "goals", "stack", "milestones", "decisions",
|
|
92
|
+
"banned_approaches", "updated_at"}
|
|
93
|
+
missing = REQUIRED_KEYS - set(ctx.keys())
|
|
94
|
+
if missing:
|
|
95
|
+
warnings.append(f"Missing keys in context file: {', '.join(sorted(missing))}")
|
|
96
|
+
|
|
97
|
+
# Field length caps
|
|
98
|
+
project = ctx.get("project", {})
|
|
99
|
+
for field in ("name", "description", "version"):
|
|
100
|
+
val = project.get(field, "")
|
|
101
|
+
if isinstance(val, str) and len(val) > 500:
|
|
102
|
+
warnings.append(f"project.{field} exceeds 500 characters \u2014 consider shortening.")
|
|
103
|
+
|
|
104
|
+
# Injection pattern check on free-text list fields
|
|
105
|
+
INJECTION_RE = _re.compile(
|
|
106
|
+
r'ignore\s+(previous|above|prior|all)|override\s+(policy|restriction|rule)|'
|
|
107
|
+
r'system\s*prompt|you\s+are\s+(now|a)|act\s+as\s+(if|a|an)|'
|
|
108
|
+
r'pretend\s+(to|that|you)|bypass\s+(security|check|restriction)|'
|
|
109
|
+
r'disregard\s+(policy|rule)|admin\s+(mode|access|override)|'
|
|
110
|
+
r'\bsudo\b|\bjailbreak\b',
|
|
111
|
+
_re.IGNORECASE,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def _check_text(label: str, text: str) -> None:
|
|
115
|
+
if INJECTION_RE.search(text):
|
|
116
|
+
warnings.append(
|
|
117
|
+
f"Possible injection pattern detected in {label}: {text[:80]!r}"
|
|
118
|
+
)
|
|
119
|
+
if len(text) > 2000:
|
|
120
|
+
warnings.append(f"{label} entry exceeds 2000 characters \u2014 review before injecting.")
|
|
121
|
+
|
|
122
|
+
for i, goal in enumerate(ctx.get("goals", [])):
|
|
123
|
+
if isinstance(goal, str):
|
|
124
|
+
_check_text(f"goals[{i}]", goal)
|
|
125
|
+
|
|
126
|
+
for i, dec in enumerate(ctx.get("decisions", [])):
|
|
127
|
+
if isinstance(dec, dict):
|
|
128
|
+
dec_dict = cast(dict[str, object], dec)
|
|
129
|
+
for fld in ("decision", "rationale"):
|
|
130
|
+
fld_val = dec_dict.get(fld)
|
|
131
|
+
if isinstance(fld_val, str):
|
|
132
|
+
_check_text(f"decisions[{i}].{fld}", fld_val)
|
|
133
|
+
elif isinstance(dec, str):
|
|
134
|
+
_check_text(f"decisions[{i}]", dec)
|
|
135
|
+
|
|
136
|
+
for i, banned in enumerate(ctx.get("banned_approaches", [])):
|
|
137
|
+
if isinstance(banned, str):
|
|
138
|
+
_check_text(f"banned_approaches[{i}]", banned)
|
|
139
|
+
|
|
140
|
+
return warnings
|
|
141
|
+
|
|
142
|
+
|
|
77
143
|
def _load() -> dict[str, Any]:
|
|
78
144
|
if not CONTEXT_PATH.exists():
|
|
79
145
|
print(
|
|
@@ -127,6 +193,11 @@ def cmd_init(args: argparse.Namespace) -> int:
|
|
|
127
193
|
|
|
128
194
|
def cmd_show(args: argparse.Namespace) -> int: # noqa: ARG001
|
|
129
195
|
ctx = _load()
|
|
196
|
+
warnings = _validate_context(ctx)
|
|
197
|
+
if warnings:
|
|
198
|
+
print("[context_manager] VALIDATION WARNINGS — review before injecting:", file=sys.stderr)
|
|
199
|
+
for w in warnings:
|
|
200
|
+
print(f" ! {w}", file=sys.stderr)
|
|
130
201
|
print(json.dumps(ctx, indent=2))
|
|
131
202
|
return 0
|
|
132
203
|
|
|
@@ -134,6 +205,12 @@ def cmd_show(args: argparse.Namespace) -> int: # noqa: ARG001
|
|
|
134
205
|
def cmd_inject(args: argparse.Namespace) -> int: # noqa: ARG001
|
|
135
206
|
"""Print a formatted block suitable for injection into an agent system prompt."""
|
|
136
207
|
ctx = _load()
|
|
208
|
+
warnings = _validate_context(ctx)
|
|
209
|
+
if warnings:
|
|
210
|
+
print("[context_manager] VALIDATION WARNINGS \u2014 context has potential issues:", file=sys.stderr)
|
|
211
|
+
for w in warnings:
|
|
212
|
+
print(f" ! {w}", file=sys.stderr)
|
|
213
|
+
print("[context_manager] Proceeding with inject, but review warnings above.", file=sys.stderr)
|
|
137
214
|
p = ctx.get("project", {})
|
|
138
215
|
|
|
139
216
|
lines: list[str] = []
|