@openwop/openwop-conformance 1.136.0 → 1.136.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.
@@ -1,28 +1,61 @@
1
1
  {
2
2
  "id": "conformance-agent-memory-cross-tenant",
3
3
  "name": "Conformance: Agent Memory Cross-Tenant Isolation (CTI-1)",
4
- "version": "1.0",
5
- "description": "Phase 3 / CTI-1. Issues a deliberately-cross-tenant memoryRef probe (a path referencing tenant B from a run owned by tenant A). Host MUST return [] / null — no leak. Probe result lands in `crossTenantProbe` variable. See agentMemoryCrossTenantIsolation.test.ts.",
4
+ "version": "1.1",
5
+ "description": "Phase 3 / CTI-1. TWO-SIDED since 2026-08-17 (S35): the `core.identity` node with `memoryAction: \"cross-tenant-probe\"` MUST (1) write one entry under the run's OWN tenant at `agent.memoryRef` and land its id in `ownerEntryId`, (2) list that owner memoryRef and land the result in `ownerProbe` — a NON-EMPTY array containing the written entry (the positive control: proves the adapter is really being exercised), and (3) issue the deliberately-cross-tenant probe at `config.probeMemoryRef` (a path owned by ANOTHER tenant) and land the result in `crossTenantProbe`, which MUST be exactly `[]` (or `null`)never another tenant's entries, and never left unset. A host may instead fail the run with a security-related error at step 3. Before S35 the scenario accepted an unset `crossTenantProbe`, so a host that ignored `memoryAction` entirely passed a critical-tier invariant vacuously. See agentMemoryCrossTenantIsolation.test.ts.",
6
6
  "nodes": [
7
7
  {
8
8
  "id": "cross-tenant-probe",
9
9
  "typeId": "core.identity",
10
10
  "name": "Cross-Tenant Memory Probe",
11
- "position": { "x": 0, "y": 0 },
11
+ "position": {
12
+ "x": 0,
13
+ "y": 0
14
+ },
12
15
  "config": {
13
16
  "memoryAction": "cross-tenant-probe",
14
17
  "probeMemoryRef": "conformance/another-tenant/agent-memory"
15
18
  },
16
- "inputs": {}
19
+ "inputs": {},
20
+ "agent": {
21
+ "agentId": "core.conformance.cti-agent",
22
+ "memoryRef": "conformance/agent-memory-cti"
23
+ }
17
24
  }
18
25
  ],
19
26
  "edges": [],
20
27
  "triggers": [
21
- { "id": "manual", "type": "manual", "enabled": true }
28
+ {
29
+ "id": "manual",
30
+ "type": "manual",
31
+ "enabled": true
32
+ }
22
33
  ],
23
34
  "variables": [
24
- { "name": "crossTenantProbe", "type": "array" }
35
+ {
36
+ "name": "ownerEntryId",
37
+ "type": "string"
38
+ },
39
+ {
40
+ "name": "ownerProbe",
41
+ "type": "array"
42
+ },
43
+ {
44
+ "name": "crossTenantProbe",
45
+ "type": "array"
46
+ }
25
47
  ],
26
- "metadata": { "tags": ["conformance", "multi-agent", "phase-3", "memory", "cti-1", "security"] },
27
- "settings": { "timeout": 10000 }
48
+ "metadata": {
49
+ "tags": [
50
+ "conformance",
51
+ "multi-agent",
52
+ "phase-3",
53
+ "memory",
54
+ "cti-1",
55
+ "security"
56
+ ]
57
+ },
58
+ "settings": {
59
+ "timeout": 10000
60
+ }
28
61
  }
@@ -1,14 +1,17 @@
1
1
  {
2
2
  "id": "conformance-agent-memory-ttl",
3
3
  "name": "Conformance: Agent Memory TTL Expiry",
4
- "version": "1.0",
5
- "description": "Phase 3. Writes two memory entries — one with `expiresAt` in the past, one in the future — then lists. The result MUST contain only the future-dated entry. List result lands in `memoryList` variable. See agentMemoryTtlExpiry.test.ts.",
4
+ "version": "1.1",
5
+ "description": "Phase 3. Writes two memory entries under `agent.memoryRef` — one with `expiresAt` in the past, one in the future — lands their ids in `expiredId` / `freshId`, then lists into `memoryList`. TWO-SIDED since 2026-08-17 (S35): the list MUST contain the entry whose id is `freshId` and MUST NOT contain the entry whose id is `expiredId`; before S35 an empty list (a host that filtered everything, or wrote nothing) passed. See agentMemoryTtlExpiry.test.ts.",
6
6
  "nodes": [
7
7
  {
8
8
  "id": "memory-ttl-probe",
9
9
  "typeId": "core.identity",
10
10
  "name": "Memory TTL Probe",
11
- "position": { "x": 0, "y": 0 },
11
+ "position": {
12
+ "x": 0,
13
+ "y": 0
14
+ },
12
15
  "config": {
13
16
  "memoryAction": "ttl-probe"
14
17
  },
@@ -21,11 +24,36 @@
21
24
  ],
22
25
  "edges": [],
23
26
  "triggers": [
24
- { "id": "manual", "type": "manual", "enabled": true }
27
+ {
28
+ "id": "manual",
29
+ "type": "manual",
30
+ "enabled": true
31
+ }
25
32
  ],
26
33
  "variables": [
27
- { "name": "memoryList", "type": "array" }
34
+ {
35
+ "name": "freshId",
36
+ "type": "string"
37
+ },
38
+ {
39
+ "name": "expiredId",
40
+ "type": "string"
41
+ },
42
+ {
43
+ "name": "memoryList",
44
+ "type": "array"
45
+ }
28
46
  ],
29
- "metadata": { "tags": ["conformance", "multi-agent", "phase-3", "memory", "ttl"] },
30
- "settings": { "timeout": 10000 }
47
+ "metadata": {
48
+ "tags": [
49
+ "conformance",
50
+ "multi-agent",
51
+ "phase-3",
52
+ "memory",
53
+ "ttl"
54
+ ]
55
+ },
56
+ "settings": {
57
+ "timeout": 10000
58
+ }
31
59
  }
package/fixtures.md CHANGED
@@ -90,9 +90,9 @@ All fixtures MUST advertise:
90
90
  | Multi-Agent Handoff (child) | `conformance-multi-agent-handoff-child` | RFC 0037 (`version: 1`) — child for `conformance-multi-agent-handoff`. Declares `childOutcome.defaultValue='handoff-complete'`; the parent's outputMapping harvests it onto `parentResult`, triggering the `output.harvested` transition event. | `completed` | ≤ 5s |
91
91
  | Multi-Agent Confidence Escalation | `conformance-multi-agent-confidence-escalation` | RFC 0039 §A (`version: 2`) — exercises the confidence-floor escalation contract. Supervisor's `mockDispatchPlan` carries ONE decision with `confidence: 0.3` (below the 0.5 spec floor). The host MUST emit `core.workflowChain.confidence-escalated` AND suspend with a clarification interrupt BEFORE any dispatch.began fires; conformance asserts zero `core.workflowChain.event` records (no dispatch). Capability-gated on `capabilities.multiAgent.executionModel.version >= 2`. | `waiting-clarification` | ≤ 30s |
92
92
  | Agent Memory Round-Trip | `conformance-agent-memory-roundtrip` | Phase 3 — `MemoryAdapter.list/get` write → read | `completed` | ≤ 15s |
93
- | Agent Memory Cross-Tenant | `conformance-agent-memory-cross-tenant` | Phase 3 / CTI-1 — cross-tenant probe MUST return `[]` / `null` | `completed` | ≤ 10s |
93
+ | Agent Memory Cross-Tenant | `conformance-agent-memory-cross-tenant` | Phase 3 / CTI-1 — TWO-SIDED (S35, 2026-08-17): owner write+list lands `ownerEntryId` + non-empty `ownerProbe` (positive control); the cross-tenant probe at `config.probeMemoryRef` lands `crossTenantProbe`, which MUST be exactly `[]` / `null` and MUST be set | `completed` | ≤ 10s |
94
94
  | Agent Memory Redaction | `conformance-agent-memory-redaction` | Phase 3 / SR-1 — BYOK plaintext surfaces as `[REDACTED:<id>]` on read | `completed` | ≤ 15s |
95
- | Agent Memory TTL | `conformance-agent-memory-ttl` | Phase 3 — `expiresAt` excludes expired entries from list/get | `completed` | ≤ 10s |
95
+ | Agent Memory TTL | `conformance-agent-memory-ttl` | Phase 3 — `expiresAt` excludes expired entries from list/get; TWO-SIDED (S35, 2026-08-17): host lands `freshId` / `expiredId`, `memoryList` MUST include the fresh id and exclude the expired id | `completed` | ≤ 10s |
96
96
  | Agent Memory Injection Budget | `conformance-agent-memory-injection-budget` | RFC 0113 — token-budgeted `MemoryAdapter.list` (`tokenBudget`/`rank`/`query`): cumulative tokens ≤ budget, over-budget single entry omitted, SR-1 + CTI-1 re-asserted on the budgeted path; `rank:'relevance'` delegates to `memory.search` semantic | `completed` | ≤ 15s |
97
97
  | Context-Budget Multi-Turn | `conformance-context-budget-multiturn` | RFC 0111 — multi-iteration `core.orchestrator.supervisor` loop whose per-turn transcript grows; a host bounding it exposes `{ tokenCounter, tokenCount, eventIds, summarizedRanges }` via the `GET /v1/host/sample/agent/transcript-window` seam (`tokenCount ≤ transcriptTokenBudget`, internally consistent, keepLastTurns verbatim); the summarization leg replays and asserts `context.summarized` → `summaryRef` reuse. Reference hosts that do not advertise `contextBudget` soft-skip | `completed` | ≤ 60s |
98
98
  | Conversation Lifecycle | `conformance-conversation-lifecycle` | Phase 4 — open → exchange → close event ordering | `completed` | ≤ 20s |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/openwop-conformance",
3
- "version": "1.136.0",
3
+ "version": "1.136.1",
4
4
  "description": "Production-ready black-box conformance suite for OpenWOP v1.0 compliant servers.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "_comment": "Provenance of this vendored schemas/ copy. See conformance/README.md \u00a7\"Resolving the contract\". Compare against the stamp in your installed @openwop/openwop-conformance to detect a stale hand-copied contract.",
3
- "suiteVersion": "1.136.0",
4
- "corpusCommit": "6e56e76c2abe74f8c56232f3163aba33bdd3db8c"
3
+ "suiteVersion": "1.136.1",
4
+ "corpusCommit": "9db354d7a8215e24252beda93be717a6413a02df"
5
5
  }
@@ -40,15 +40,37 @@ describe.skipIf(SKIP)('agentMemoryCrossTenantIsolation: CTI-1 invariant', () =>
40
40
  if (terminal.status === 'completed') {
41
41
  const snap = await driver.get(`/v1/runs/${encodeURIComponent(runId)}`);
42
42
  const body = snap.json as { variables?: Record<string, unknown> };
43
- const crossTenantResult = body.variables?.crossTenantProbe as
44
- | Array<unknown>
45
- | null
46
- | undefined;
47
- // Cross-tenant list MUST return [] (or null); never another tenant's entries.
43
+ const vars = body.variables ?? {};
44
+
45
+ // S35 (2026-08-17): POSITIVE CONTROL first. Before this, the only assertion
46
+ // was "crossTenantProbe is empty or falsy" — a host whose core.identity node
47
+ // ignored `config.memoryAction` entirely left the variable undefined and
48
+ // PASSED a critical-tier invariant vacuously (measured by openwop-app H49).
49
+ // The owner-side write+list proves the MemoryAdapter is really exercised.
50
+ const ownerProbe = vars.ownerProbe;
51
+ expect(
52
+ Array.isArray(ownerProbe) && ownerProbe.length > 0,
53
+ driver.describe('agent-memory.md §CTI-1 / fixtures.md conformance-agent-memory-cross-tenant', 'ownerProbe MUST be a NON-EMPTY array — the run\'s own tenant can read the entry it just wrote (positive control; an unset or empty ownerProbe means the probe never ran)'),
54
+ ).toBe(true);
55
+ const ownerEntryId = vars.ownerEntryId;
56
+ if (typeof ownerEntryId === 'string' && ownerEntryId.length > 0) {
57
+ expect(
58
+ (ownerProbe as Array<{ id?: unknown }>).some((e) => e && typeof e === 'object' && e.id === ownerEntryId),
59
+ driver.describe('agent-memory.md §CTI-1', `ownerProbe MUST contain the entry the host reports writing (ownerEntryId=${ownerEntryId})`),
60
+ ).toBe(true);
61
+ }
62
+
63
+ // Cross-tenant list MUST return exactly [] (or null); never another
64
+ // tenant's entries, and never left unset.
65
+ const crossTenantResult = vars.crossTenantProbe;
66
+ expect(
67
+ crossTenantResult !== undefined,
68
+ driver.describe('agent-memory.md §CTI-1', 'crossTenantProbe MUST be set — an unset variable means the cross-tenant probe was never issued, which proves nothing'),
69
+ ).toBe(true);
48
70
  if (Array.isArray(crossTenantResult)) {
49
- expect(crossTenantResult.length).toBe(0);
71
+ expect(crossTenantResult.length, driver.describe('agent-memory.md §CTI-1', 'a cross-tenant memoryRef probe MUST return [] — no entries of another tenant')).toBe(0);
50
72
  } else {
51
- expect(crossTenantResult).toBeFalsy();
73
+ expect(crossTenantResult, driver.describe('agent-memory.md §CTI-1', 'a non-array cross-tenant probe result MUST be null')).toBeNull();
52
74
  }
53
75
  }
54
76
  });
@@ -44,5 +44,23 @@ describe.skipIf(SKIP)('agentMemoryTtlExpiry: expired entries are excluded from l
44
44
  expect(new Date(e.expiresAt).getTime()).toBeGreaterThan(now);
45
45
  }
46
46
  }
47
+ // S35 (2026-08-17): TWO-SIDED. `[]` used to pass (a host that filtered
48
+ // everything, or wrote nothing and returned []). The fixture now lands the
49
+ // ids of both entries; the fresh one MUST be present, the expired one MUST
50
+ // be absent.
51
+ const freshId = body.variables?.freshId;
52
+ const expiredId = body.variables?.expiredId;
53
+ expect(
54
+ typeof freshId === 'string' && freshId.length > 0 && typeof expiredId === 'string' && expiredId.length > 0,
55
+ driver.describe('fixtures.md conformance-agent-memory-ttl', 'the host MUST land freshId and expiredId (the ids of the two entries it wrote) so the list can be checked on both sides'),
56
+ ).toBe(true);
57
+ expect(
58
+ listResult!.some((e) => e.id === freshId),
59
+ driver.describe('agent-memory.md §TTL', `list() MUST include the fresh entry (freshId=${String(freshId)})`),
60
+ ).toBe(true);
61
+ expect(
62
+ listResult!.some((e) => e.id === expiredId),
63
+ driver.describe('agent-memory.md §TTL', `list() MUST NOT include the expired entry (expiredId=${String(expiredId)})`),
64
+ ).toBe(false);
47
65
  });
48
66
  });