@moreih29/nexus-core 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -6
- package/conformance/lifecycle/README.md +7 -1
- package/conformance/lifecycle/memory-access-record.json +27 -0
- package/conformance/lifecycle/session-end.json +48 -0
- package/conformance/schema/fixture.schema.json +2 -2
- package/docs/consumer-implementation-guide.md +56 -55
- package/manifest.json +62 -62
- package/package.json +1 -1
- package/scripts/lib/validate.ts +2 -0
package/README.md
CHANGED
|
@@ -11,29 +11,30 @@ Nexus 생태계는 세 층위로 나뉩니다. `nexus-core`는 가장 아래, **
|
|
|
11
11
|
```
|
|
12
12
|
Supervision (reserved)
|
|
13
13
|
│ read-only
|
|
14
|
-
Execution claude-nexus ↔ opencode-nexus
|
|
14
|
+
Execution claude-nexus ↔ opencode-nexus ↔ codex-nexus
|
|
15
15
|
│ read-only
|
|
16
16
|
Authoring nexus-core ← 이 저장소
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
현재 active 소비자는
|
|
19
|
+
현재 active 소비자는 세 Execution layer 하네스(`claude-nexus`, `opencode-nexus`, `codex-nexus`)이며, 모두 `nexus-core`를 **read-only**로 참조합니다. Supervision layer는 외부 감독자 consumer를 위해 예약된 자리입니다(과거 nexus-code 프로젝트가 이 layer를 구현했으나 2026-04-14 archived).
|
|
20
20
|
|
|
21
21
|
| Consumer | Layer | 하는 일 |
|
|
22
22
|
|---|---|---|
|
|
23
23
|
| [`claude-nexus`](https://github.com/moreih29/claude-nexus) | Execution | Claude Code 하네스 위에서 에이전트 조립·디스패치 |
|
|
24
24
|
| [`opencode-nexus`](https://github.com/moreih29/opencode-nexus) | Execution | OpenCode 하네스 위에서 에이전트 조립·디스패치 |
|
|
25
|
+
| [`codex-nexus`](https://github.com/moreih29/codex-nexus) | Execution | Codex 하네스 위에서 에이전트 조립·디스패치 |
|
|
25
26
|
|
|
26
27
|
## For Consumer Repositories
|
|
27
28
|
|
|
28
|
-
> 이 저장소는 **외부 사용자가 직접 설치하는 플러그인이 아닙니다**. Nexus 하네스(`claude-nexus`, `opencode-nexus`)를 사용하려면 해당 저장소의 안내를 따르세요.
|
|
29
|
+
> 이 저장소는 **외부 사용자가 직접 설치하는 플러그인이 아닙니다**. Nexus 하네스(`claude-nexus`, `opencode-nexus`, `codex-nexus`)를 사용하려면 해당 저장소의 안내를 따르세요.
|
|
29
30
|
|
|
30
|
-
Consumer 저장소(`claude-nexus`, `opencode-nexus`)의 LLM 에이전트가 `@moreih29/nexus-core` 버전 업그레이드를 처리해야 하는 경우, **[CONSUMING.md](./CONSUMING.md)**의 Upgrade Protocol을 참조하세요.
|
|
31
|
+
Consumer 저장소(`claude-nexus`, `opencode-nexus`, `codex-nexus`)의 LLM 에이전트가 `@moreih29/nexus-core` 버전 업그레이드를 처리해야 하는 경우, **[CONSUMING.md](./CONSUMING.md)**의 Upgrade Protocol을 참조하세요.
|
|
31
32
|
|
|
32
33
|
CONSUMING.md는 LLM 에이전트 전용 문서입니다. 사람 독자는 이 README가 더 유용합니다.
|
|
33
34
|
|
|
34
35
|
## 이 저장소는 무엇이 **아닌가**
|
|
35
36
|
|
|
36
|
-
`nexus-core`는 **외부 사용자가 직접 설치하는 플러그인이 아닙니다.** Nexus 하네스(`claude-nexus`, `opencode-nexus`)를 사용하고 싶다면 해당 저장소의 안내를 따르세요. `nexus-core`는 그
|
|
37
|
+
`nexus-core`는 **외부 사용자가 직접 설치하는 플러그인이 아닙니다.** Nexus 하네스(`claude-nexus`, `opencode-nexus`, `codex-nexus`)를 사용하고 싶다면 해당 저장소의 안내를 따르세요. `nexus-core`는 그 세 하네스가 내부적으로 공유하는 자산입니다.
|
|
37
38
|
|
|
38
39
|
## 범위
|
|
39
40
|
|
|
@@ -69,7 +70,7 @@ CONSUMING.md는 LLM 에이전트 전용 문서입니다. 사람 독자는 이 RE
|
|
|
69
70
|
|
|
70
71
|
## Status
|
|
71
72
|
|
|
72
|
-
v0.
|
|
73
|
+
v0.11.0 (2026-04-17). 최신 release: 훅 컨텍스트 주입 가이드 governance 재정립 (GH #21/#22/#23 — 3 consumer drift 대응). 상세 변경 이력은 [CHANGELOG.md](./CHANGELOG.md) 참조.
|
|
73
74
|
|
|
74
75
|
## References
|
|
75
76
|
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
| `agent-spawn.json` | `agent_spawn` | `agent-tracker.json` 첫 항목 생성 (running 상태) |
|
|
10
10
|
| `agent-complete.json` | `agent_complete` | `agent-tracker.json` 항목 완료 상태 전환 |
|
|
11
11
|
| `agent-resume.json` | `agent_resume` | `agent-tracker.json` 재개 카운터 및 상태 복귀 |
|
|
12
|
+
| `session-end.json` | `session_end` | `agent-tracker.json` 삭제, `history.json` · `memory/` · `context/` · `rules/` 보존 |
|
|
13
|
+
|
|
14
|
+
## Why session-end was re-added at v0.11.0
|
|
15
|
+
|
|
16
|
+
v0.6.0에서 session-start/session-end fixture를 제거한 이유는 당시 구현이 `runtime.schema.json`에 의존했기 때문이다. `runtime.schema.json`은 execution semantics를 포함하므로 nexus-core의 prompt-only 원칙과 충돌했다. v0.11.0 재도입은 `runtime.schema.json`을 완전히 배제하고 `agent-tracker.schema.json`과 `history.schema.json`만 참조한다. session_end fixture가 검증하는 핵심 invariant는 두 가지다: (1) agent-tracker.json은 세션 종료 시 삭제된다(session-scoped), (2) history.json · memory/ · context/ · rules/는 삭제되어서는 안 된다(Negative MUST — cross-session knowledge 보존).
|
|
12
17
|
|
|
13
18
|
## Tool-action 대신 Event 트리거 사용
|
|
14
19
|
|
|
@@ -31,7 +36,7 @@ Test runner는 이 키를 파일 시스템 경로로 해석하기 전에 다음
|
|
|
31
36
|
|
|
32
37
|
치환 규약:
|
|
33
38
|
- `{STATE_ROOT}`와 `{HARNESS_ID}` 두 token만 인식된다. 그 외 `{…}` 형태의 token이 경로에 등장하면 authoring 오류로 처리한다.
|
|
34
|
-
- 공통 파일 fixture (`plan-*`, `task-*`, `history-*`, `artifact-write`)는 하드코딩된 경로를 사용하며 이 token 규약이 적용되지 않는다. Token 경로는 lifecycle fixture
|
|
39
|
+
- 공통 파일 fixture (`plan-*`, `task-*`, `history-*`, `artifact-write`)는 하드코딩된 경로를 사용하며 이 token 규약이 적용되지 않는다. Token 경로는 lifecycle fixture 4종(`agent-spawn`, `agent-complete`, `agent-resume`, `session-end`)의 `agent-tracker.json` 경로에만 적용된다.
|
|
35
40
|
|
|
36
41
|
### Fixture별 경로 요약
|
|
37
42
|
|
|
@@ -40,3 +45,4 @@ Test runner는 이 키를 파일 시스템 경로로 해석하기 전에 다음
|
|
|
40
45
|
| `agent-spawn.json` | `claude-nexus` | `.nexus/state/claude-nexus/agent-tracker.json` |
|
|
41
46
|
| `agent-complete.json` | `claude-nexus` | `.nexus/state/claude-nexus/agent-tracker.json` |
|
|
42
47
|
| `agent-resume.json` | `claude-nexus` | `.nexus/state/claude-nexus/agent-tracker.json` |
|
|
48
|
+
| `session-end.json` | `claude-nexus` | `.nexus/state/claude-nexus/agent-tracker.json` (null assertion — file must not exist after session end) |
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"test_id": "memory_access_record",
|
|
3
|
+
"description": "Verifies that a memory-access.jsonl record is written with the required fields when an agent reads a memory file during a session",
|
|
4
|
+
"precondition": {
|
|
5
|
+
"state_files": {
|
|
6
|
+
"{STATE_ROOT}/{HARNESS_ID}/memory-access.jsonl": null
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
"event": {
|
|
10
|
+
"type": "session_end",
|
|
11
|
+
"params": {
|
|
12
|
+
"harness_id": "claude-nexus"
|
|
13
|
+
},
|
|
14
|
+
"description": "Harness reads memory file and appends an access record to memory-access.jsonl. This fixture validates the structure of the resulting JSONL record against memory-access.schema.json."
|
|
15
|
+
},
|
|
16
|
+
"postcondition": {
|
|
17
|
+
"state_files": {
|
|
18
|
+
"{STATE_ROOT}/{HARNESS_ID}/memory-access.jsonl": {}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"covers": {
|
|
22
|
+
"state_schemas": {
|
|
23
|
+
"memory-access.schema.json": ["path", "last_accessed_ts", "access_count", "last_agent", "schema_version"]
|
|
24
|
+
},
|
|
25
|
+
"description": "memory-access.jsonl is a JSONL file where each line is a memory-access.schema.json record. This fixture asserts the file exists after a session where memory was read. Content is not inspected via JSONPath because JSONL is not parseable as a single JSON object; field coverage is declared here to satisfy the schema coverage gate."
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"test_id": "session_end",
|
|
3
|
+
"description": "Verifies that session_end deletes agent-tracker.json while preserving history.json and the memory/context/rules knowledge directories",
|
|
4
|
+
"precondition": {
|
|
5
|
+
"state_files": {
|
|
6
|
+
"{STATE_ROOT}/{HARNESS_ID}/agent-tracker.json": [
|
|
7
|
+
{
|
|
8
|
+
"harness_id": "claude-nexus",
|
|
9
|
+
"agent_name": "architect",
|
|
10
|
+
"agent_id": "uuid-eng01",
|
|
11
|
+
"started_at": "2026-04-13T00:00:00.000Z",
|
|
12
|
+
"resume_count": 0,
|
|
13
|
+
"status": "completed",
|
|
14
|
+
"stopped_at": "2026-04-13T01:00:00.000Z",
|
|
15
|
+
"last_message": "Implementation complete",
|
|
16
|
+
"files_touched": ["src/foo.ts"]
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
".nexus/history.json": {},
|
|
20
|
+
".nexus/memory/lessons.md": {},
|
|
21
|
+
".nexus/context/architecture.md": {},
|
|
22
|
+
".nexus/rules/project.md": {}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"event": {
|
|
26
|
+
"type": "session_end",
|
|
27
|
+
"params": {
|
|
28
|
+
"harness_id": "claude-nexus"
|
|
29
|
+
},
|
|
30
|
+
"description": "Harness teardown on session close: agent-tracker registry is deleted (session-scoped), while history, memory, context, and rules are retained across sessions"
|
|
31
|
+
},
|
|
32
|
+
"postcondition": {
|
|
33
|
+
"state_files": {
|
|
34
|
+
"{STATE_ROOT}/{HARNESS_ID}/agent-tracker.json": null,
|
|
35
|
+
".nexus/history.json": {},
|
|
36
|
+
".nexus/memory/lessons.md": {},
|
|
37
|
+
".nexus/context/architecture.md": {},
|
|
38
|
+
".nexus/rules/project.md": {}
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"covers": {
|
|
42
|
+
"state_schemas": {
|
|
43
|
+
"agent-tracker.schema.json": ["harness_id", "agent_name", "agent_id", "started_at", "resume_count", "status", "stopped_at", "last_message", "files_touched[]"],
|
|
44
|
+
"history.schema.json": ["cycles"]
|
|
45
|
+
},
|
|
46
|
+
"description": "session_end fixture verifies the deletion boundary: agent-tracker.json (session-scoped) is deleted, history.json existence is asserted via empty-object check (file must exist, content not re-validated here). memory/context/rules files are verified present via empty-object check, confirming the do-not-delete MUST constraint. agent-tracker fields are listed here for completeness; leaf-field coverage is already established by agent-spawn/agent-complete/agent-resume fixtures."
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -166,8 +166,8 @@
|
|
|
166
166
|
"properties": {
|
|
167
167
|
"type": {
|
|
168
168
|
"type": "string",
|
|
169
|
-
"enum": ["agent_spawn", "agent_complete", "agent_resume"],
|
|
170
|
-
"description": "Lifecycle event type. agent_spawn/agent_complete/agent_resume: agent instance lifecycle."
|
|
169
|
+
"enum": ["agent_spawn", "agent_complete", "agent_resume", "session_end"],
|
|
170
|
+
"description": "Lifecycle event type. agent_spawn/agent_complete/agent_resume: agent instance lifecycle. session_end: session teardown — agent-tracker.json deleted, history/memory/context/rules preserved."
|
|
171
171
|
},
|
|
172
172
|
"params": {
|
|
173
173
|
"type": "object",
|
|
@@ -439,12 +439,19 @@ User types "[plan] analyze the architecture"
|
|
|
439
439
|
- Also read skills/{skill_id}/meta.yml to check for harness_docs_refs
|
|
440
440
|
|
|
|
441
441
|
v
|
|
442
|
-
4.
|
|
443
|
-
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
442
|
+
4. Skill Activation
|
|
443
|
+
- Consumers SHOULD prefer the harness's native skill-registration
|
|
444
|
+
or command-dispatch primitive — any mechanism by which the harness
|
|
445
|
+
natively exposes user-invocable behavioral modes to the LLM —
|
|
446
|
+
over raw prompt concatenation. The skill body.md content becomes
|
|
447
|
+
the registered skill's operating specification.
|
|
448
|
+
- Only when no such native primitive exists, fall back to context injection:
|
|
449
|
+
a. System prompt injection
|
|
450
|
+
b. Tool-response injection
|
|
451
|
+
c. User-message prefix
|
|
452
|
+
- The name and concrete shape of the native primitive are consumer-owned;
|
|
453
|
+
nexus-core specifies only that such a mechanism, when available, is
|
|
454
|
+
the preferred activation path.
|
|
448
455
|
- Also inject harness-specific context for any harness_docs_refs declared in meta.yml
|
|
449
456
|
- The body.md content BECOMES the LLM's operating instructions for this skill
|
|
450
457
|
|
|
|
@@ -503,82 +510,77 @@ The canonical trigger for every tag is the explicit bracket form in `vocabulary/
|
|
|
503
510
|
|
|
504
511
|
Hooks are the consumer's mechanism for responding to lifecycle events. nexus-core defines 8 abstract events. The names are harness-neutral; each harness maps them to its own event API.
|
|
505
512
|
|
|
506
|
-
###
|
|
513
|
+
### Relationship to harness-native knowledge surfaces
|
|
507
514
|
|
|
508
|
-
|
|
515
|
+
Consumer harnesses typically maintain a harness-native primary knowledge surface — for example, a system-prompt layer, a persistent session notice, or a harness banner injected before every LLM call. The hook events and their guidance in §9 are **complementary to** this harness-native surface, not a replacement for it. When §9 says "inject X at hook Y", the concrete injection path may live in the harness-native surface if that produces an equivalent effect. What matters is that the described information reaches the agent at the described moment — the delivery mechanism is consumer-local. Each bullet in §9 describes behavior the consumer implements in hook handler code — specifying what prompt context should be injected at the hook's firing moment. These specifications are authored for consumer developers; nexus-core does not inject §9 prose itself into LLM runtime context.
|
|
509
516
|
|
|
510
|
-
|
|
511
|
-
- **OpenCode**: its own hook API names — map accordingly when building an OpenCode consumer
|
|
517
|
+
### Event mapping examples
|
|
512
518
|
|
|
513
|
-
Identify the equivalent events in your harness's plugin system and implement the expected behaviors below.
|
|
519
|
+
Different harnesses expose these events under different names. Identify the equivalent events in your harness's plugin system and implement the expected behaviors below.
|
|
514
520
|
|
|
515
521
|
### 8 lifecycle events
|
|
516
522
|
|
|
517
523
|
#### `session_start`
|
|
518
524
|
|
|
519
|
-
**When it fires:**
|
|
525
|
+
**When it fires:** A new agent session begins (harness runtime may expose this as `SessionStart`, `session.created`, an init hook, or equivalent).
|
|
520
526
|
|
|
521
527
|
**Expected consumer behavior:**
|
|
522
|
-
-
|
|
523
|
-
-
|
|
524
|
-
-
|
|
525
|
-
- On v0.7.0+, harnesses SHOULD silently remove any legacy `.nexus/state/agent-tracker.json` (root) at session start. The file is session-scoped and legacy records are safely discarded.
|
|
526
|
-
- Check for stale state from a prior crashed session: if `plan.json` or `tasks.json` exist, warn the user that these may be leftover from an unclean shutdown.
|
|
527
|
-
- Load the knowledge index: list files in `.nexus/memory/`, `.nexus/context/`, and `.nexus/rules/` to build the reference index that will be injected into subagent spawns.
|
|
528
|
+
- **SHOULD** create `.nexus/` and `.nexus/state/` directories if they do not exist.
|
|
529
|
+
- **SHOULD** write `.nexus/.gitignore` with `state/` if it does not exist.
|
|
530
|
+
- **MUST** create `.nexus/state/{harness-id}/` if it does not exist, then initialize `.nexus/state/{harness-id}/agent-tracker.json` as `[]`. `agent-tracker.json` is a shared-purpose session file whose path is namespaced per harness to prevent cross-harness collisions (see [nexus-outputs-contract.md §Shared filename convention](./nexus-outputs-contract.md)).
|
|
531
|
+
- On v0.7.0+, harnesses **SHOULD** silently remove any legacy `.nexus/state/agent-tracker.json` (root) at session start. The file is session-scoped and legacy records are safely discarded.
|
|
528
532
|
|
|
529
533
|
---
|
|
530
534
|
|
|
531
535
|
#### `user_message`
|
|
532
536
|
|
|
533
|
-
**When it fires:** The user submits a message to
|
|
537
|
+
**When it fires:** The user submits a message and it is about to be processed (harness runtime may expose this as `UserPromptSubmit`, `message.received`, an on-input hook, or equivalent).
|
|
534
538
|
|
|
535
539
|
**Expected consumer behavior:**
|
|
536
|
-
-
|
|
540
|
+
- **SHOULD** scan the message text for bracket tags. Match against all triggers defined in `vocabulary/tags.yml`.
|
|
537
541
|
- For each matched tag:
|
|
538
|
-
-
|
|
539
|
-
|
|
540
|
-
- After tag routing, inject contextual guidance into Lead's available context before LLM inference begins:
|
|
542
|
+
- **SHOULD** activate the skill if `type=skill` (see §8, steps 3–5). Do not proceed with normal message handling for that tag.
|
|
543
|
+
- **SHOULD** inject contextual guidance into Lead's available context before LLM inference begins:
|
|
541
544
|
- Current plan status: if `plan.json` exists, summarize pending vs. decided issues.
|
|
542
545
|
- Task progress: if `tasks.json` exists, summarize total/completed/pending counts and the ready-task set.
|
|
543
|
-
|
|
544
|
-
- If no tags are matched, pass the message to Lead without modification.
|
|
546
|
+
- **SHOULD** pass the message to Lead without modification if no tags are matched.
|
|
545
547
|
|
|
546
548
|
---
|
|
547
549
|
|
|
548
550
|
#### `subagent_spawn`
|
|
549
551
|
|
|
550
|
-
**When it fires:**
|
|
552
|
+
**When it fires:** A subagent is created and about to begin execution (harness runtime may expose this as `SubagentStart`, `agent.spawned`, a subagent-init hook, or equivalent).
|
|
551
553
|
|
|
552
554
|
**Expected consumer behavior:**
|
|
553
|
-
-
|
|
554
|
-
-
|
|
555
|
-
-
|
|
556
|
-
-
|
|
557
|
-
- Pass the structured task context to the agent: title, context, approach, and acceptance criteria (see §10, Context Passing).
|
|
555
|
+
- **MUST** record the new agent entry in `.nexus/state/{harness-id}/agent-tracker.json`: `{ harness_id, agent_name, agent_id, started_at }`.
|
|
556
|
+
- **SHOULD** inject the knowledge index into the subagent's initial context: the list of files in `.nexus/memory/`, `.nexus/context/`, and `.nexus/rules/` so the agent knows what project knowledge is available.
|
|
557
|
+
- **SHOULD** apply capability restrictions: resolve `effective_capabilities` for this agent type and configure the subagent's tool access accordingly (see §6).
|
|
558
|
+
- **SHOULD** apply the resume evaluation: check `owner_reuse_policy` on the task and the agent's `resume_tier` to determine whether to spawn fresh or resume a prior session (see §10).
|
|
558
559
|
|
|
559
560
|
---
|
|
560
561
|
|
|
561
562
|
#### `subagent_complete`
|
|
562
563
|
|
|
563
|
-
**When it fires:** A subagent finishes its assigned work and returns control
|
|
564
|
+
**When it fires:** A subagent finishes its assigned work and returns control (harness runtime may expose this as `SubagentStop`, `agent.completed`, a subagent-exit hook, or equivalent).
|
|
564
565
|
|
|
565
566
|
**Expected consumer behavior:**
|
|
566
|
-
-
|
|
567
|
-
-
|
|
568
|
-
-
|
|
569
|
-
-
|
|
570
|
-
- Update the task status in `tasks.json` via the `task_update` tool: set to `completed`.
|
|
567
|
+
- **MUST** update `.nexus/state/{harness-id}/agent-tracker.json`: set `status=completed`, record `stopped_at` timestamp.
|
|
568
|
+
- **MUST** compute `files_touched` from your tool-log or the subagent's tool usage record. Record which files were created or modified in the `files_touched` array of the agent's `agent-tracker.json` entry. This field is the authoritative source for bounded-tier resume evaluation.
|
|
569
|
+
- **SHOULD** surface an unfinished-task warning when the stopping agent has tasks left in `pending` or `in_progress` state. This pattern is observed across consumers and aids recovery; the warning may be injected into Lead's context or surfaced as a reminder.
|
|
570
|
+
- **MAY** update the task status in `tasks.json` when the agent was assigned a specific task. Note: this responsibility may alternatively be fulfilled at the Lead or tool-contract layer rather than the hook surface.
|
|
571
571
|
|
|
572
572
|
---
|
|
573
573
|
|
|
574
574
|
#### `pre_tool_use`
|
|
575
575
|
|
|
576
|
-
**When it fires:** A tool is about to execute.
|
|
576
|
+
**When it fires:** A tool call has been issued and is about to execute (harness runtime may expose this as `PreToolUse`, `tool.before`, a pre-call interceptor, or equivalent).
|
|
577
577
|
|
|
578
578
|
**Expected consumer behavior:**
|
|
579
|
-
-
|
|
580
|
-
- Capability
|
|
581
|
-
-
|
|
579
|
+
- **SHOULD** enforce a gate for unplanned file edits: if `tasks.json` does not exist and the tool is a file-editing tool, block the call and return an error explaining that edits outside of a planned task cycle are disallowed. This prevents unplanned workspace changes.
|
|
580
|
+
- **Capability invariant (MUST)**: disallowed tools MUST NOT execute. **Enforcement layer (consumer choice)**: The abstract invariant holds regardless of enforcement layer. Consumers may enforce at the hook, in static agent config (frontmatter/TOML/disallowedTools), at a framework-level gate, or any combination — the choice is consumer-local. What nexus-core requires is the invariant, not the enforcement mechanism.
|
|
581
|
+
- **MAY** apply any other pre-condition checks your harness requires (rate limits, sandbox policies, etc.).
|
|
582
|
+
|
|
583
|
+
> **SHOULD note on block reason**: When a tool call is blocked, the `reason` field returned to the LLM is consumed as operational guidance for the next action. Consumers SHOULD compose the `reason` as actionable, directive text rather than a generic error message.
|
|
582
584
|
|
|
583
585
|
Read-only tools (query tools, status reads) are never blocked by capability gates. Only tools with primary write effects are subject to capability restrictions.
|
|
584
586
|
|
|
@@ -586,41 +588,40 @@ Read-only tools (query tools, status reads) are never blocked by capability gate
|
|
|
586
588
|
|
|
587
589
|
#### `post_tool_use`
|
|
588
590
|
|
|
589
|
-
**When it fires:** A tool has
|
|
591
|
+
**When it fires:** A tool call has completed and a result is available (harness runtime may expose this as `PostToolUse`, `tool.after`, a post-call interceptor, or equivalent).
|
|
590
592
|
|
|
591
593
|
**Expected consumer behavior:**
|
|
592
|
-
-
|
|
593
|
-
-
|
|
594
|
-
- If the tool result indicates an error, record the error in the log for diagnostic purposes. Do not suppress error results.
|
|
594
|
+
- **SHOULD** append a log entry to `.nexus/state/tool-log.jsonl`: timestamp, agent_id, tool name, file path (if a file was touched), result status. (This practice is observed across all consumers; nexus-core does not yet define a schema for this file — schema definition pending.)
|
|
595
|
+
- **MAY** record the error in the log for diagnostic purposes if the tool result indicates an error. Do not suppress error results.
|
|
595
596
|
|
|
596
597
|
---
|
|
597
598
|
|
|
598
599
|
#### `session_end`
|
|
599
600
|
|
|
600
|
-
**When it fires:** The
|
|
601
|
+
**When it fires:** The session is terminating (harness runtime may expose this as `Stop`, `session.end`, a shutdown hook, or equivalent).
|
|
601
602
|
|
|
602
603
|
**Expected consumer behavior:**
|
|
603
|
-
-
|
|
604
|
-
-
|
|
605
|
-
-
|
|
606
|
-
-
|
|
607
|
-
-
|
|
604
|
+
- **SHOULD** warn the user if `tasks.json` exists and contains incomplete tasks (status `pending` or `in_progress`), that the session is ending with unfinished work and suggest calling `task_close` to archive before exiting.
|
|
605
|
+
- **SHOULD** delete `.nexus/state/{harness-id}/agent-tracker.json` (a session-scoped file that has no value beyond the session).
|
|
606
|
+
- **MAY** rotate or archive `tool-log.jsonl` if your harness supports log retention.
|
|
607
|
+
- **MUST NOT** delete `history.json`, `memory/`, `context/`, or `rules/` — these are project-scoped and must persist across sessions.
|
|
608
|
+
- **SHOULD** prompt or block with an "all tasks completed — call `task_close` to archive" reminder when `tasks.json` shows all tasks completed but the cycle has not been archived via `task_close`. Run-cycle closure is a recoverability boundary.
|
|
608
609
|
|
|
609
610
|
---
|
|
610
611
|
|
|
611
612
|
#### `context_compact`
|
|
612
613
|
|
|
613
|
-
**When it fires:** The
|
|
614
|
+
**When it fires:** The runtime compresses the context window (harness runtime may expose this as `PostCompact`, `context.compacted`, a post-compact hook, or equivalent).
|
|
614
615
|
|
|
615
616
|
**Expected consumer behavior:**
|
|
616
|
-
-
|
|
617
|
+
- **SHOULD** re-inject the critical session snapshot that was lost in compression:
|
|
617
618
|
- Active skill/mode: which skill is currently active (plan, run, sync, or none).
|
|
618
619
|
- Plan status: if `plan.json` exists, re-inject the issue list with pending/decided status.
|
|
619
620
|
- Task progress: if `tasks.json` exists, re-inject the task list with status and ready-task set.
|
|
620
621
|
- Knowledge file index: re-inject the list of files in `.nexus/memory/`, `.nexus/context/`, `.nexus/rules/`.
|
|
621
622
|
- Active agent list: re-inject which subagents are currently tracked in `.nexus/state/{harness-id}/agent-tracker.json`.
|
|
622
|
-
- Context compaction is a context loss event
|
|
623
|
-
-
|
|
623
|
+
- **SHOULD** restore state from the state files on disk. Context compaction is a context loss event; the LLM cannot reconstruct session state from its compressed context alone.
|
|
624
|
+
- **SHOULD** read state files fresh from disk — do not rely on in-memory caches that may also have been cleared.
|
|
624
625
|
|
|
625
626
|
---
|
|
626
627
|
|
package/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"nexus_core_version": "0.
|
|
3
|
-
"nexus_core_commit": "
|
|
2
|
+
"nexus_core_version": "0.12.0",
|
|
3
|
+
"nexus_core_commit": "f2aae4883f354d1d0e5ee06f163c96c2b0f9bea3",
|
|
4
4
|
"schema_contract_version": "2.0",
|
|
5
5
|
"agents": [
|
|
6
6
|
{
|
|
@@ -19,6 +19,22 @@
|
|
|
19
19
|
"id": "architect",
|
|
20
20
|
"body_hash": "sha256:85f9a3de419f32cdae284436eb1d902bff19a2230c50fe3068ffc642949a63b7"
|
|
21
21
|
},
|
|
22
|
+
{
|
|
23
|
+
"name": "designer",
|
|
24
|
+
"description": "UX/UI design — evaluates user experience, interaction patterns, and how users will experience the product",
|
|
25
|
+
"task": "UI/UX design, interaction patterns, user experience",
|
|
26
|
+
"alias_ko": "디자이너",
|
|
27
|
+
"category": "how",
|
|
28
|
+
"resume_tier": "persistent",
|
|
29
|
+
"model_tier": "high",
|
|
30
|
+
"capabilities": [
|
|
31
|
+
"no_file_edit",
|
|
32
|
+
"no_task_create",
|
|
33
|
+
"no_task_update"
|
|
34
|
+
],
|
|
35
|
+
"id": "designer",
|
|
36
|
+
"body_hash": "sha256:88ac56147d0e5bdf23fa591ce570a9c2d0eb1338df4ec2219f6238ddfcb65df4"
|
|
37
|
+
},
|
|
22
38
|
{
|
|
23
39
|
"name": "engineer",
|
|
24
40
|
"description": "Implementation — writes code, debugs issues, follows specifications from Lead and architect",
|
|
@@ -34,10 +50,10 @@
|
|
|
34
50
|
"body_hash": "sha256:3d58b1b490c2f93cace2eedd0f04ec000f84514388eb086768cf53f8fa33db01"
|
|
35
51
|
},
|
|
36
52
|
{
|
|
37
|
-
"name": "
|
|
38
|
-
"description": "
|
|
39
|
-
"task": "
|
|
40
|
-
"alias_ko": "
|
|
53
|
+
"name": "postdoc",
|
|
54
|
+
"description": "Research methodology and synthesis — designs investigation approach, evaluates evidence quality, writes synthesis documents",
|
|
55
|
+
"task": "Research methodology, evidence synthesis",
|
|
56
|
+
"alias_ko": "포닥",
|
|
41
57
|
"category": "how",
|
|
42
58
|
"resume_tier": "persistent",
|
|
43
59
|
"model_tier": "high",
|
|
@@ -46,24 +62,23 @@
|
|
|
46
62
|
"no_task_create",
|
|
47
63
|
"no_task_update"
|
|
48
64
|
],
|
|
49
|
-
"id": "
|
|
50
|
-
"body_hash": "sha256:
|
|
65
|
+
"id": "postdoc",
|
|
66
|
+
"body_hash": "sha256:da9b8c2568b8b5812abed6d6324139f814379d48dc63cdc5d0b5b263f5407814"
|
|
51
67
|
},
|
|
52
68
|
{
|
|
53
|
-
"name": "
|
|
54
|
-
"description": "
|
|
55
|
-
"task": "
|
|
56
|
-
"alias_ko": "
|
|
57
|
-
"category": "
|
|
69
|
+
"name": "researcher",
|
|
70
|
+
"description": "Independent investigation — conducts web searches, gathers evidence, and reports findings with citations",
|
|
71
|
+
"task": "Web search, independent investigation",
|
|
72
|
+
"alias_ko": "리서처",
|
|
73
|
+
"category": "do",
|
|
58
74
|
"resume_tier": "persistent",
|
|
59
|
-
"model_tier": "
|
|
75
|
+
"model_tier": "standard",
|
|
60
76
|
"capabilities": [
|
|
61
77
|
"no_file_edit",
|
|
62
|
-
"no_task_create"
|
|
63
|
-
"no_task_update"
|
|
78
|
+
"no_task_create"
|
|
64
79
|
],
|
|
65
|
-
"id": "
|
|
66
|
-
"body_hash": "sha256:
|
|
80
|
+
"id": "researcher",
|
|
81
|
+
"body_hash": "sha256:fc79bafec05503327bd51a0b84b6e642d304bd79c45b78db6448b112793c143e"
|
|
67
82
|
},
|
|
68
83
|
{
|
|
69
84
|
"name": "reviewer",
|
|
@@ -81,25 +96,10 @@
|
|
|
81
96
|
"body_hash": "sha256:f04d15249601b14046e7e40a4475defb289436c4474afbd89986964f8c3e7c2f"
|
|
82
97
|
},
|
|
83
98
|
{
|
|
84
|
-
"name": "
|
|
85
|
-
"description": "
|
|
86
|
-
"task": "
|
|
87
|
-
"alias_ko": "
|
|
88
|
-
"category": "do",
|
|
89
|
-
"resume_tier": "persistent",
|
|
90
|
-
"model_tier": "standard",
|
|
91
|
-
"capabilities": [
|
|
92
|
-
"no_file_edit",
|
|
93
|
-
"no_task_create"
|
|
94
|
-
],
|
|
95
|
-
"id": "researcher",
|
|
96
|
-
"body_hash": "sha256:fc79bafec05503327bd51a0b84b6e642d304bd79c45b78db6448b112793c143e"
|
|
97
|
-
},
|
|
98
|
-
{
|
|
99
|
-
"name": "postdoc",
|
|
100
|
-
"description": "Research methodology and synthesis — designs investigation approach, evaluates evidence quality, writes synthesis documents",
|
|
101
|
-
"task": "Research methodology, evidence synthesis",
|
|
102
|
-
"alias_ko": "포닥",
|
|
99
|
+
"name": "strategist",
|
|
100
|
+
"description": "Business strategy — evaluates market positioning, competitive landscape, and business viability of decisions",
|
|
101
|
+
"task": "Business strategy, market analysis, competitive positioning",
|
|
102
|
+
"alias_ko": "전략가",
|
|
103
103
|
"category": "how",
|
|
104
104
|
"resume_tier": "persistent",
|
|
105
105
|
"model_tier": "high",
|
|
@@ -108,8 +108,8 @@
|
|
|
108
108
|
"no_task_create",
|
|
109
109
|
"no_task_update"
|
|
110
110
|
],
|
|
111
|
-
"id": "
|
|
112
|
-
"body_hash": "sha256:
|
|
111
|
+
"id": "strategist",
|
|
112
|
+
"body_hash": "sha256:0254b4144a22c66209bd68119553d9057a4fb7f9b1ff7ebb9878687d99583465"
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"name": "tester",
|
|
@@ -142,19 +142,6 @@
|
|
|
142
142
|
}
|
|
143
143
|
],
|
|
144
144
|
"skills": [
|
|
145
|
-
{
|
|
146
|
-
"name": "nx-run",
|
|
147
|
-
"description": "Execution — user-directed agent composition.",
|
|
148
|
-
"summary": "Execution — user-directed agent composition",
|
|
149
|
-
"triggers": [
|
|
150
|
-
"run"
|
|
151
|
-
],
|
|
152
|
-
"harness_docs_refs": [
|
|
153
|
-
"resume_invocation"
|
|
154
|
-
],
|
|
155
|
-
"id": "nx-run",
|
|
156
|
-
"body_hash": "sha256:0e2c443efceeab4621709a85cd4e2ba50471d2e850680c655d776cbb62814549"
|
|
157
|
-
},
|
|
158
145
|
{
|
|
159
146
|
"name": "nx-init",
|
|
160
147
|
"description": "Project onboarding — scan, mission, essentials, context generation",
|
|
@@ -167,16 +154,6 @@
|
|
|
167
154
|
"id": "nx-init",
|
|
168
155
|
"body_hash": "sha256:b828a974ab4722dd7f1d15a4338d1380fdae47cd42c1bd4a5539277075efb6fc"
|
|
169
156
|
},
|
|
170
|
-
{
|
|
171
|
-
"name": "nx-sync",
|
|
172
|
-
"description": "Context knowledge synchronization — scans project state and updates .nexus/context/ design documents",
|
|
173
|
-
"summary": "Context knowledge synchronization",
|
|
174
|
-
"triggers": [
|
|
175
|
-
"sync"
|
|
176
|
-
],
|
|
177
|
-
"id": "nx-sync",
|
|
178
|
-
"body_hash": "sha256:3ee8dd780d53f2e04472de6c701e16bc1fbde7f2ce9ed4e680b7cd2010530a22"
|
|
179
|
-
},
|
|
180
157
|
{
|
|
181
158
|
"name": "nx-plan",
|
|
182
159
|
"description": "Structured multi-perspective analysis to decompose issues, align on decisions, and produce an enriched plan before execution. Plan only — does not execute.",
|
|
@@ -189,6 +166,29 @@
|
|
|
189
166
|
],
|
|
190
167
|
"id": "nx-plan",
|
|
191
168
|
"body_hash": "sha256:083ce49c06f8d3e4a0299aa8eb8e33460b68d6a277fe356b4db9635c21016aff"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"name": "nx-run",
|
|
172
|
+
"description": "Execution — user-directed agent composition.",
|
|
173
|
+
"summary": "Execution — user-directed agent composition",
|
|
174
|
+
"triggers": [
|
|
175
|
+
"run"
|
|
176
|
+
],
|
|
177
|
+
"harness_docs_refs": [
|
|
178
|
+
"resume_invocation"
|
|
179
|
+
],
|
|
180
|
+
"id": "nx-run",
|
|
181
|
+
"body_hash": "sha256:0e2c443efceeab4621709a85cd4e2ba50471d2e850680c655d776cbb62814549"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"name": "nx-sync",
|
|
185
|
+
"description": "Context knowledge synchronization — scans project state and updates .nexus/context/ design documents",
|
|
186
|
+
"summary": "Context knowledge synchronization",
|
|
187
|
+
"triggers": [
|
|
188
|
+
"sync"
|
|
189
|
+
],
|
|
190
|
+
"id": "nx-sync",
|
|
191
|
+
"body_hash": "sha256:3ee8dd780d53f2e04472de6c701e16bc1fbde7f2ce9ed4e680b7cd2010530a22"
|
|
192
192
|
}
|
|
193
193
|
],
|
|
194
194
|
"vocabulary": {
|
package/package.json
CHANGED
package/scripts/lib/validate.ts
CHANGED
|
@@ -645,6 +645,7 @@ export async function generateManifest(
|
|
|
645
645
|
return { ...meta, body_hash };
|
|
646
646
|
})
|
|
647
647
|
);
|
|
648
|
+
agentEntries.sort((a, b) => a.id.localeCompare(b.id));
|
|
648
649
|
|
|
649
650
|
const skillEntries: ManifestSkill[] = await Promise.all(
|
|
650
651
|
skills.map(async ({ meta, dir }) => {
|
|
@@ -652,6 +653,7 @@ export async function generateManifest(
|
|
|
652
653
|
return { ...meta, body_hash };
|
|
653
654
|
})
|
|
654
655
|
);
|
|
656
|
+
skillEntries.sort((a, b) => a.id.localeCompare(b.id));
|
|
655
657
|
|
|
656
658
|
const invocationSummaries: ManifestInvocationEntry[] = vocab.invocations.map((inv) => ({
|
|
657
659
|
id: inv.id,
|