network-ai 4.4.2 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -115,6 +115,7 @@ Match your problem to the Network-AI primitive:
115
115
  | Race conditions in parallel agent writes | `LockedBlackboard` with `priority-wins` | Higher-priority agents preempt lower-priority writes on conflict |
116
116
  | Need to expose all tools to an AI via MCP | `McpSseServer` + `network-ai-server` | HTTP/SSE server at `GET /sse`, `POST /mcp`, `GET /tools` |
117
117
  | Runtime AI control of the orchestrator | `ControlMcpTools` | AI can read/set config, spawn/stop agents, drive FSM transitions |
118
+ | Agents lose project context between sessions | `ProjectContextManager` (`context_manager.py`) | Persistent Layer-3 JSON file injected into every agent's system prompt |
118
119
 
119
120
  ---
120
121
 
package/QUICKSTART.md CHANGED
@@ -445,6 +445,67 @@ python scripts/revoke_token.py --cleanup
445
445
  python scripts/validate_token.py --token "grant_85364b44..."
446
446
  ```
447
447
 
448
+ ### Project Context (Layer 3 — Persistent Memory)
449
+
450
+ Long-lived project state that every agent inherits, regardless of session:
451
+
452
+ ```bash
453
+ # Initialise once
454
+ python scripts/context_manager.py init \
455
+ --name "MyProject" \
456
+ --description "Multi-agent workflow" \
457
+ --version "1.0.0"
458
+
459
+ # Print formatted block to inject into agent system prompts
460
+ python scripts/context_manager.py inject
461
+
462
+ # Record an architecture decision
463
+ python scripts/context_manager.py update \
464
+ --section decisions \
465
+ --add '{"decision": "Use atomic blackboard commits", "rationale": "Prevent race conditions"}'
466
+
467
+ # Update milestones
468
+ python scripts/context_manager.py update --section milestones --complete "Ship v1.0"
469
+ python scripts/context_manager.py update --section milestones --add '{"planned": "Vector memory integration"}'
470
+
471
+ # Set tech stack
472
+ python scripts/context_manager.py update --section stack --set '{"language": "TypeScript", "runtime": "Node.js 18"}'
473
+
474
+ # Add a goal or ban an approach
475
+ python scripts/context_manager.py update --section goals --add "Ship v2.0 before Q3"
476
+ python scripts/context_manager.py update --section banned --add "Direct DB writes from agents"
477
+
478
+ # Print full context as JSON
479
+ python scripts/context_manager.py show
480
+ ```
481
+
482
+ Context is stored in `data/project-context.json`. The `inject` command outputs a markdown block ready to prepend to any agent's system prompt.
483
+
484
+ ---
485
+
486
+ ## Use with Claude, ChatGPT & Codex
487
+
488
+ Three integration files are included in the repo root:
489
+
490
+ | File | Use |
491
+ |---|---|
492
+ | [`claude-tools.json`](claude-tools.json) | Claude API tool use & OpenAI Codex — drop into the `tools` array |
493
+ | [`openapi.yaml`](openapi.yaml) | Custom GPT Actions — import directly in the GPT editor |
494
+ | [`claude-project-prompt.md`](claude-project-prompt.md) | Claude Projects — paste into Custom Instructions |
495
+
496
+ **Claude API / Codex:**
497
+ ```js
498
+ import tools from './claude-tools.json' assert { type: 'json' };
499
+ // Pass tools array to anthropic.messages.create({ tools }) or OpenAI chat completions
500
+ ```
501
+
502
+ **Custom GPT Actions:**
503
+ In the GPT editor → Actions → Import from URL, or paste `openapi.yaml` directly.
504
+ Set the server URL to your running `npx network-ai-server --port 3001` instance.
505
+
506
+ **Claude Projects:**
507
+ Copy the contents of `claude-project-prompt.md` into a Claude Project's Custom Instructions field. No server required for instruction-only mode.
508
+
448
509
  ---
449
510
 
450
511
  ## Fan-Out / Fan-In Pattern
package/SKILL.md CHANGED
@@ -33,6 +33,24 @@ metadata:
33
33
 
34
34
  > **Scope of this skill bundle:** All instructions below run local Python scripts (`scripts/*.py`). No network calls are made by this skill. Tokens are UUID-based (`grant_{uuid4().hex}`) stored in `data/active_grants.json`. Audit logging is plain JSONL (`data/audit_log.jsonl`) — no HMAC signing in the Python layer. HMAC-signed tokens, AES-256 encryption, and the standalone MCP server are all features of the **companion Node.js package** (`npm install -g network-ai`) — they are **not** implemented in these Python scripts and do **not** run automatically.
35
35
 
36
+ ## Setup
37
+
38
+ **No pip install required.** All 5 scripts use Python standard library only — zero third-party packages.
39
+
40
+ ```bash
41
+ # Prerequisite: python3 (any version ≥ 3.8)
42
+ python3 --version
43
+
44
+ # That's it. Run any script directly:
45
+ python3 scripts/blackboard.py list
46
+ python3 scripts/swarm_guard.py budget-init --task-id "task_001" --budget 10000
47
+
48
+ # Optional: for cross-platform file locking on Windows production hosts
49
+ pip install filelock # only needed if you see locking issues on Windows
50
+ ```
51
+
52
+ The `data/` directory is created automatically on first run. No configuration files, environment variables, or credentials are required.
53
+
36
54
  Multi-agent coordination system for complex workflows requiring task delegation, parallel execution, and permission-controlled access to sensitive APIs.
37
55
 
38
56
  ## 🎯 Orchestrator System Instructions
@@ -147,6 +165,85 @@ python {baseDir}/scripts/blackboard.py write "task:001:final" \
147
165
 
148
166
  ---
149
167
 
168
+ ## The 3-Layer Memory Model
169
+
170
+ Every agent in the swarm operates with three memory layers, each with a different scope and lifetime:
171
+
172
+ | Layer | Name | Lifetime | Managed by |
173
+ |-------|------|----------|------------|
174
+ | **1** | Agent context | Ephemeral — current task only | Platform (per-session) |
175
+ | **2** | Blackboard | TTL-scoped — shared across agents | `scripts/blackboard.py` |
176
+ | **3** | Project context | Persistent — survives all sessions | `scripts/context_manager.py` |
177
+
178
+ ### Layer 1 — Agent Context
179
+ Each agent's own context window: the current task instructions, conversation history, and immediate working memory. Managed automatically by the OpenClaw/LLM platform. Nothing to configure.
180
+
181
+ ### Layer 2 — Blackboard (Shared Coordination State)
182
+ A shared markdown file (`swarm-blackboard.md`) for real-time cross-agent coordination: task results, grant tokens, status flags, and TTL-scoped cache entries. Agents read and write via `scripts/blackboard.py`. Entries expire automatically.
183
+
184
+ ### Layer 3 — Project Context (Persistent Long-Term Memory)
185
+ A JSON file (`data/project-context.json`) that holds information every agent should know, regardless of what session or task is running:
186
+ - **Goals** — long-term objectives of the project
187
+ - **Tech stack** — languages, frameworks, infrastructure
188
+ - **Milestones** — completed, in-progress, and planned work
189
+ - **Architecture decisions** — design choices and their rationales
190
+ - **Banned approaches** — approaches that have been ruled out
191
+
192
+ #### Initialising Project Context
193
+
194
+ ```bash
195
+ python {baseDir}/scripts/context_manager.py init \
196
+ --name "MyProject" \
197
+ --description "Multi-agent workflow automation" \
198
+ --version "1.0.0"
199
+ ```
200
+
201
+ #### Injecting Context into an Agent System Prompt
202
+
203
+ ```bash
204
+ python {baseDir}/scripts/context_manager.py inject
205
+ ```
206
+
207
+ Copy the output block to the top of your agent's system prompt. Every agent that receives this block shares the same long-term project awareness.
208
+
209
+ #### Recording a Decision
210
+
211
+ ```bash
212
+ python {baseDir}/scripts/context_manager.py update \
213
+ --section decisions \
214
+ --add '{"decision": "Use atomic blackboard commits", "rationale": "Prevent race conditions in parallel agents"}'
215
+ ```
216
+
217
+ #### Updating Milestones
218
+
219
+ ```bash
220
+ # Mark a milestone complete
221
+ python {baseDir}/scripts/context_manager.py update \
222
+ --section milestones --complete "Ship v2.0"
223
+
224
+ # Add a planned milestone
225
+ python {baseDir}/scripts/context_manager.py update \
226
+ --section milestones --add '{"planned": "Integrate vector memory"}'
227
+ ```
228
+
229
+ #### Setting the Tech Stack
230
+
231
+ ```bash
232
+ python {baseDir}/scripts/context_manager.py update \
233
+ --section stack \
234
+ --set '{"language": "TypeScript", "runtime": "Node.js 18", "framework": "Network-AI v4.5"}'
235
+ ```
236
+
237
+ #### Banning an Approach
238
+
239
+ ```bash
240
+ python {baseDir}/scripts/context_manager.py update \
241
+ --section banned \
242
+ --add "Direct database writes from agent scripts (use permission gating)"
243
+ ```
244
+
245
+ ---
246
+
150
247
  ## When to Use This Skill
151
248
 
152
249
  - **Task Delegation**: Route work to specialized agents (data_analyst, strategy_advisor, risk_assessor)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "network-ai",
3
- "version": "4.4.2",
3
+ "version": "4.5.0",
4
4
  "description": "AI agent orchestration framework for TypeScript/Node.js - 14 adapters (LangChain, AutoGen, CrewAI, OpenAI Assistants, LlamaIndex, Semantic Kernel, Haystack, DSPy, Agno, MCP, OpenClaw, A2A, Codex + streaming variants). Built-in CLI, security, swarm intelligence, real-time streaming, and agentic workflow patterns.",
5
5
  "homepage": "https://github.com/jovanSAPFIONEER/Network-AI#readme",
6
6
  "main": "dist/index.js",
@@ -0,0 +1,334 @@
1
+ #!/usr/bin/env python3
2
+ # SECURITY: This script makes NO network calls and spawns NO subprocesses.
3
+ # All I/O is local file operations only:
4
+ # READS: data/project-context.json, data/audit_log.jsonl
5
+ # WRITES: data/project-context.json, data/audit_log.jsonl
6
+ # Imports used: argparse, json, sys, datetime, pathlib, typing
7
+ # No imports of: requests, socket, subprocess, urllib, http, ssl, ftplib, smtplib
8
+ """
9
+ Project Context Manager - Persistent Layer-3 Memory for Agent Swarms
10
+
11
+ Maintains a JSON file that stores long-lived project context: goals, architecture
12
+ decisions, tech stack, milestones, and banned approaches. This context is injected
13
+ into every agent session so all agents share the same project-level awareness,
14
+ regardless of what's currently on the short-term blackboard.
15
+
16
+ THE 3-LAYER MEMORY MODEL
17
+ Layer 1 — Agent context : current task, immediate instructions (ephemeral, per-agent)
18
+ Layer 2 — Blackboard : task results, grants, coordination state (shared, TTL-scoped)
19
+ Layer 3 — Project context : architecture decisions, goals, stack, milestones (THIS FILE)
20
+
21
+ Usage:
22
+ python context_manager.py init --name "MyProject" [--description "..."] [--version "1.0.0"]
23
+ python context_manager.py show
24
+ python context_manager.py inject
25
+ python context_manager.py update --section decisions --add '{"decision": "...", "rationale": "..."}'
26
+ python context_manager.py update --section milestones --complete "task name"
27
+ python context_manager.py update --section milestones --add '{"planned": "task name"}'
28
+ python context_manager.py update --section stack --set '{"language": "TypeScript"}'
29
+ python context_manager.py update --section goals --add "Ship v2.0 before Q3"
30
+ python context_manager.py update --section banned --add "Direct DB writes from agents"
31
+
32
+ Examples:
33
+ python context_manager.py init --name "Network-AI" --description "Multi-agent swarm framework" --version "4.5.0"
34
+ python context_manager.py update --section decisions --add '{"decision": "Use atomic blackboard commits", "rationale": "Prevent race conditions"}'
35
+ python context_manager.py update --section milestones --complete "v4.4.3 ClawHub clean-scan"
36
+ python context_manager.py inject
37
+ """
38
+
39
+ import argparse
40
+ import json
41
+ import sys
42
+ from datetime import datetime, timezone
43
+ from pathlib import Path
44
+ from typing import Any, cast
45
+
46
+ CONTEXT_PATH = Path(__file__).parent.parent / "data" / "project-context.json"
47
+ AUDIT_LOG_PATH = Path(__file__).parent.parent / "data" / "audit_log.jsonl"
48
+
49
+ EMPTY_CONTEXT: dict[str, Any] = {
50
+ "project": {
51
+ "name": "",
52
+ "description": "",
53
+ "version": ""
54
+ },
55
+ "goals": [],
56
+ "stack": {},
57
+ "milestones": {
58
+ "completed": [],
59
+ "in_progress": [],
60
+ "planned": []
61
+ },
62
+ "decisions": [],
63
+ "banned_approaches": [],
64
+ "agents": {},
65
+ "updated_at": ""
66
+ }
67
+
68
+
69
+ # ---------------------------------------------------------------------------
70
+ # Helpers
71
+ # ---------------------------------------------------------------------------
72
+
73
+ def _now_iso() -> str:
74
+ return datetime.now(timezone.utc).isoformat()
75
+
76
+
77
+ def _load() -> dict[str, Any]:
78
+ if not CONTEXT_PATH.exists():
79
+ print(
80
+ f"[context_manager] No project context found at {CONTEXT_PATH}.\n"
81
+ "Run: python context_manager.py init --name \"YourProject\"",
82
+ file=sys.stderr
83
+ )
84
+ sys.exit(1)
85
+ with CONTEXT_PATH.open("r", encoding="utf-8") as fh:
86
+ return json.load(fh)
87
+
88
+
89
+ def _save(ctx: dict[str, Any]) -> None:
90
+ ctx["updated_at"] = _now_iso()
91
+ CONTEXT_PATH.parent.mkdir(parents=True, exist_ok=True)
92
+ with CONTEXT_PATH.open("w", encoding="utf-8") as fh:
93
+ json.dump(ctx, fh, indent=2)
94
+ fh.write("\n")
95
+
96
+
97
+ def _audit(action: str, detail: dict[str, Any]) -> None:
98
+ entry: dict[str, Any] = {
99
+ "timestamp": _now_iso(),
100
+ "action": action,
101
+ "details": {"source": "context_manager", **detail}
102
+ }
103
+ AUDIT_LOG_PATH.parent.mkdir(parents=True, exist_ok=True)
104
+ with AUDIT_LOG_PATH.open("a", encoding="utf-8") as fh:
105
+ fh.write(json.dumps(entry) + "\n")
106
+
107
+
108
+ # ---------------------------------------------------------------------------
109
+ # Commands
110
+ # ---------------------------------------------------------------------------
111
+
112
+ def cmd_init(args: argparse.Namespace) -> int:
113
+ if CONTEXT_PATH.exists():
114
+ print(f"[context_manager] Context file already exists at {CONTEXT_PATH}.")
115
+ print("Use 'update' to change individual sections, or delete the file to reinitialise.")
116
+ return 1
117
+
118
+ ctx = json.loads(json.dumps(EMPTY_CONTEXT)) # deep copy
119
+ ctx["project"]["name"] = args.name
120
+ ctx["project"]["description"] = args.description or ""
121
+ ctx["project"]["version"] = args.version or ""
122
+ _save(ctx)
123
+ _audit("init", {"name": args.name, "version": args.version})
124
+ print(f"[context_manager] Project context initialised: {CONTEXT_PATH}")
125
+ return 0
126
+
127
+
128
+ def cmd_show(args: argparse.Namespace) -> int: # noqa: ARG001
129
+ ctx = _load()
130
+ print(json.dumps(ctx, indent=2))
131
+ return 0
132
+
133
+
134
+ def cmd_inject(args: argparse.Namespace) -> int: # noqa: ARG001
135
+ """Print a formatted block suitable for injection into an agent system prompt."""
136
+ ctx = _load()
137
+ p = ctx.get("project", {})
138
+
139
+ lines: list[str] = []
140
+ lines.append("## Project Context (Layer 3 — Persistent Memory)")
141
+ lines.append("")
142
+
143
+ if p.get("name"):
144
+ name_str = p["name"]
145
+ if p.get("version"):
146
+ name_str += f" v{p['version']}"
147
+ lines.append(f"**Project:** {name_str}")
148
+ if p.get("description"):
149
+ lines.append(f"**Description:** {p['description']}")
150
+ lines.append("")
151
+
152
+ goals = ctx.get("goals", [])
153
+ if goals:
154
+ lines.append("### Goals")
155
+ for g in goals:
156
+ lines.append(f"- {g}")
157
+ lines.append("")
158
+
159
+ stack = ctx.get("stack", {})
160
+ if stack:
161
+ lines.append("### Tech Stack")
162
+ for k, v in stack.items():
163
+ lines.append(f"- **{k}**: {v}")
164
+ lines.append("")
165
+
166
+ milestones = ctx.get("milestones", {})
167
+ in_progress = milestones.get("in_progress", [])
168
+ planned = milestones.get("planned", [])
169
+ completed = milestones.get("completed", [])
170
+ if in_progress or planned or completed:
171
+ lines.append("### Milestones")
172
+ for item in in_progress:
173
+ lines.append(f"- 🔄 {item} *(in progress)*")
174
+ for item in planned:
175
+ lines.append(f"- ⏳ {item}")
176
+ for item in completed:
177
+ lines.append(f"- ✅ {item}")
178
+ lines.append("")
179
+
180
+ decisions = ctx.get("decisions", [])
181
+ if decisions:
182
+ lines.append("### Architecture Decisions")
183
+ for d in decisions:
184
+ if isinstance(d, dict):
185
+ d_typed: dict[str, Any] = cast(dict[str, Any], d)
186
+ dec: str = str(d_typed.get("decision", d))
187
+ rat: str = str(d_typed.get("rationale", ""))
188
+ lines.append(f"- **{dec}**" + (f" — {rat}" if rat else ""))
189
+ else:
190
+ lines.append(f"- {d}")
191
+ lines.append("")
192
+
193
+ banned = ctx.get("banned_approaches", [])
194
+ if banned:
195
+ lines.append("### Banned Approaches")
196
+ for b in banned:
197
+ lines.append(f"- ❌ {b}")
198
+ lines.append("")
199
+
200
+ lines.append(f"*Context last updated: {ctx.get('updated_at', 'unknown')}*")
201
+
202
+ print("\n".join(lines))
203
+ return 0
204
+
205
+
206
+ def cmd_update(args: argparse.Namespace) -> int:
207
+ ctx = _load()
208
+ section = args.section
209
+
210
+ if section == "decisions":
211
+ if not args.add:
212
+ print("[context_manager] --add is required for section 'decisions'", file=sys.stderr)
213
+ return 1
214
+ entry: Any = json.loads(args.add)
215
+ ctx.setdefault("decisions", []).append(entry)
216
+ _audit("update_decisions", {"added": entry})
217
+
218
+ elif section == "milestones":
219
+ milestones = ctx.setdefault("milestones", {"completed": [], "in_progress": [], "planned": []})
220
+ if args.complete:
221
+ name = args.complete
222
+ # Move from in_progress or planned → completed
223
+ for bucket in ("in_progress", "planned"):
224
+ lst: list[Any] = milestones.setdefault(bucket, [])
225
+ if name in lst:
226
+ lst.remove(name)
227
+ milestones.setdefault("completed", []).append(name)
228
+ _audit("milestone_complete", {"name": name})
229
+ elif args.add:
230
+ entry: Any = json.loads(args.add)
231
+ if isinstance(entry, dict):
232
+ for bucket in ("planned", "in_progress", "completed"):
233
+ if bucket in entry:
234
+ milestones.setdefault(bucket, []).append(entry[bucket])
235
+ _audit("milestone_add", {"bucket": bucket, "name": entry[bucket]})
236
+ else:
237
+ milestones.setdefault("planned", []).append(str(entry))
238
+ _audit("milestone_add", {"bucket": "planned", "name": str(entry)})
239
+ else:
240
+ print("[context_manager] Provide --add or --complete for section 'milestones'", file=sys.stderr)
241
+ return 1
242
+
243
+ elif section == "stack":
244
+ if not args.set:
245
+ print("[context_manager] --set is required for section 'stack'", file=sys.stderr)
246
+ return 1
247
+ updates = json.loads(args.set)
248
+ ctx.setdefault("stack", {}).update(updates)
249
+ _audit("update_stack", {"updates": updates})
250
+
251
+ elif section == "goals":
252
+ if not args.add:
253
+ print("[context_manager] --add is required for section 'goals'", file=sys.stderr)
254
+ return 1
255
+ ctx.setdefault("goals", []).append(args.add)
256
+ _audit("update_goals", {"added": args.add})
257
+
258
+ elif section == "banned":
259
+ if not args.add:
260
+ print("[context_manager] --add is required for section 'banned'", file=sys.stderr)
261
+ return 1
262
+ ctx.setdefault("banned_approaches", []).append(args.add)
263
+ _audit("update_banned", {"added": args.add})
264
+
265
+ elif section == "project":
266
+ if not args.set:
267
+ print("[context_manager] --set is required for section 'project'", file=sys.stderr)
268
+ return 1
269
+ updates = json.loads(args.set)
270
+ ctx.setdefault("project", {}).update(updates)
271
+ _audit("update_project", {"updates": updates})
272
+
273
+ else:
274
+ print(f"[context_manager] Unknown section '{section}'. "
275
+ "Valid: decisions, milestones, stack, goals, banned, project", file=sys.stderr)
276
+ return 1
277
+
278
+ _save(ctx)
279
+ print(f"[context_manager] Section '{section}' updated.")
280
+ return 0
281
+
282
+
283
+ # ---------------------------------------------------------------------------
284
+ # CLI
285
+ # ---------------------------------------------------------------------------
286
+
287
+ def build_parser() -> argparse.ArgumentParser:
288
+ parser = argparse.ArgumentParser(
289
+ prog="context_manager.py",
290
+ description="Project Context Manager — Layer-3 persistent memory for agent swarms"
291
+ )
292
+ sub = parser.add_subparsers(dest="command", required=True)
293
+
294
+ # init
295
+ p_init = sub.add_parser("init", help="Initialise a new project context file")
296
+ p_init.add_argument("--name", required=True, help="Project name")
297
+ p_init.add_argument("--description", default="", help="Short project description")
298
+ p_init.add_argument("--version", default="", help="Current project version")
299
+
300
+ # show
301
+ sub.add_parser("show", help="Print the full context as JSON")
302
+
303
+ # inject
304
+ sub.add_parser("inject", help="Print formatted context for agent system-prompt injection")
305
+
306
+ # update
307
+ p_update = sub.add_parser("update", help="Update a specific context section")
308
+ p_update.add_argument(
309
+ "--section", required=True,
310
+ choices=["decisions", "milestones", "stack", "goals", "banned", "project"],
311
+ help="Section to update"
312
+ )
313
+ p_update.add_argument("--add", help="JSON string or plain string to append")
314
+ p_update.add_argument("--set", help="JSON object to merge/set (used by stack and project)")
315
+ p_update.add_argument("--complete", help="Mark a milestone as completed (milestones section)")
316
+
317
+ return parser
318
+
319
+
320
+ def main() -> int:
321
+ parser = build_parser()
322
+ args = parser.parse_args()
323
+
324
+ dispatch = {
325
+ "init": cmd_init,
326
+ "show": cmd_show,
327
+ "inject": cmd_inject,
328
+ "update": cmd_update,
329
+ }
330
+ return dispatch[args.command](args)
331
+
332
+
333
+ if __name__ == "__main__":
334
+ sys.exit(main())