@agent-native/creative-context 0.1.0 → 0.3.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.
Files changed (105) hide show
  1. package/agent-native.package.json +11 -0
  2. package/dist/actions/continue-context-import.d.ts +16 -1
  3. package/dist/actions/continue-context-import.js +4 -1
  4. package/dist/actions/continue-context-import.js.map +1 -1
  5. package/dist/actions/get-creative-context.d.ts +38 -0
  6. package/dist/actions/get-creative-context.d.ts.map +1 -1
  7. package/dist/actions/get-creative-context.js +59 -23
  8. package/dist/actions/get-creative-context.js.map +1 -1
  9. package/dist/actions/get-google-picker-session.d.ts +10 -0
  10. package/dist/actions/get-google-picker-session.d.ts.map +1 -0
  11. package/dist/actions/get-google-picker-session.js +38 -0
  12. package/dist/actions/get-google-picker-session.js.map +1 -0
  13. package/dist/actions/index.d.ts.map +1 -1
  14. package/dist/actions/index.js +2 -0
  15. package/dist/actions/index.js.map +1 -1
  16. package/dist/actions/start-context-enrichment.d.ts +16 -1
  17. package/dist/actions/start-context-enrichment.js +6 -4
  18. package/dist/actions/start-context-enrichment.js.map +1 -1
  19. package/dist/actions/start-context-import.d.ts +16 -1
  20. package/dist/actions/start-context-import.js +2 -1
  21. package/dist/actions/start-context-import.js.map +1 -1
  22. package/dist/client/CreativeContextPanel.d.ts +1 -59
  23. package/dist/client/CreativeContextPanel.d.ts.map +1 -1
  24. package/dist/client/CreativeContextPanel.js +111 -36
  25. package/dist/client/CreativeContextPanel.js.map +1 -1
  26. package/dist/client/actions.d.ts +8 -0
  27. package/dist/client/actions.d.ts.map +1 -1
  28. package/dist/client/actions.js +4 -0
  29. package/dist/client/actions.js.map +1 -1
  30. package/dist/client/google-slides-picker.d.ts +19 -0
  31. package/dist/client/google-slides-picker.d.ts.map +1 -0
  32. package/dist/client/google-slides-picker.js +106 -0
  33. package/dist/client/google-slides-picker.js.map +1 -0
  34. package/dist/client/index.d.ts +1 -1
  35. package/dist/client/index.d.ts.map +1 -1
  36. package/dist/client/index.js +1 -1
  37. package/dist/client/index.js.map +1 -1
  38. package/dist/client/messages.d.ts +1 -1
  39. package/dist/client/messages.d.ts.map +1 -1
  40. package/dist/client/messages.js +22 -0
  41. package/dist/client/messages.js.map +1 -1
  42. package/dist/connectors/google-slides.d.ts +1 -1
  43. package/dist/connectors/google-slides.d.ts.map +1 -1
  44. package/dist/connectors/google-slides.js +1 -2
  45. package/dist/connectors/google-slides.js.map +1 -1
  46. package/dist/connectors/normalize.d.ts +7 -0
  47. package/dist/connectors/normalize.d.ts.map +1 -1
  48. package/dist/connectors/normalize.js +122 -3
  49. package/dist/connectors/normalize.js.map +1 -1
  50. package/dist/schema/index.d.ts +30 -30
  51. package/dist/server/enrichment.d.ts +1 -0
  52. package/dist/server/enrichment.d.ts.map +1 -1
  53. package/dist/server/enrichment.js +26 -24
  54. package/dist/server/enrichment.js.map +1 -1
  55. package/dist/server/generation-context.d.ts +1 -1
  56. package/dist/server/generation-context.d.ts.map +1 -1
  57. package/dist/server/generation-context.js +1 -1
  58. package/dist/server/generation-context.js.map +1 -1
  59. package/dist/store/content.d.ts +13 -0
  60. package/dist/store/content.d.ts.map +1 -1
  61. package/dist/store/content.js +172 -1
  62. package/dist/store/content.js.map +1 -1
  63. package/dist/store/jobs.d.ts.map +1 -1
  64. package/dist/store/jobs.js +2 -2
  65. package/dist/store/jobs.js.map +1 -1
  66. package/dist/store/purge.d.ts.map +1 -1
  67. package/dist/store/purge.js +9 -8
  68. package/dist/store/purge.js.map +1 -1
  69. package/dist/store/suggestions.d.ts +1 -0
  70. package/dist/store/suggestions.d.ts.map +1 -1
  71. package/dist/store/suggestions.js +56 -38
  72. package/dist/store/suggestions.js.map +1 -1
  73. package/docs/skills/creative-context/SKILL.md +8 -0
  74. package/package.json +3 -3
  75. package/src/actions/continue-context-import.ts +4 -1
  76. package/src/actions/creative-context-a2a.spec.ts +98 -0
  77. package/src/actions/get-creative-context.spec.ts +121 -1
  78. package/src/actions/get-creative-context.ts +78 -29
  79. package/src/actions/get-google-picker-session.spec.ts +62 -0
  80. package/src/actions/get-google-picker-session.ts +45 -0
  81. package/src/actions/index.ts +2 -0
  82. package/src/actions/job-public-serialization.spec.ts +122 -0
  83. package/src/actions/start-context-enrichment.ts +6 -4
  84. package/src/actions/start-context-import.ts +2 -1
  85. package/src/client/CreativeContextPanel.tsx +163 -64
  86. package/src/client/actions.ts +18 -0
  87. package/src/client/google-slides-picker.spec.ts +36 -0
  88. package/src/client/google-slides-picker.ts +146 -0
  89. package/src/client/index.ts +2 -0
  90. package/src/client/messages.ts +35 -0
  91. package/src/client/source-config.spec.ts +24 -10
  92. package/src/connectors/google-slides.ts +1 -2
  93. package/src/connectors/normalize.spec.ts +144 -0
  94. package/src/connectors/normalize.ts +201 -3
  95. package/src/server/enrichment.spec.ts +94 -0
  96. package/src/server/enrichment.ts +27 -24
  97. package/src/server/generation-artifact-access.spec.ts +104 -0
  98. package/src/server/generation-context-a2a.spec.ts +20 -2
  99. package/src/server/generation-context.ts +6 -6
  100. package/src/server/isolated-a2a.spec.ts +21 -1
  101. package/src/store/access.integration.spec.ts +511 -26
  102. package/src/store/content.ts +213 -1
  103. package/src/store/jobs.ts +2 -0
  104. package/src/store/purge.ts +18 -9
  105. package/src/store/suggestions.ts +67 -41
@@ -0,0 +1,104 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ const mocks = vi.hoisted(() => ({
4
+ assertAccess: vi.fn(),
5
+ getRequestUserEmail: vi.fn(),
6
+ getRequestOrgId: vi.fn(),
7
+ }));
8
+
9
+ vi.mock("@agent-native/core/sharing", () => ({
10
+ assertAccess: mocks.assertAccess,
11
+ }));
12
+
13
+ vi.mock("@agent-native/core/server/request-context", () => ({
14
+ getRequestUserEmail: mocks.getRequestUserEmail,
15
+ getRequestOrgId: mocks.getRequestOrgId,
16
+ }));
17
+
18
+ import {
19
+ assertGenerationArtifactAccessProof,
20
+ createGenerationArtifactAccessCapability,
21
+ verifyGenerationArtifactAccessCapability,
22
+ } from "./generation-artifact-access.js";
23
+
24
+ const originalKey = process.env.CREATIVE_CONTEXT_A2A_KEY;
25
+ const identity = {
26
+ appId: "slides",
27
+ artifactType: "deck",
28
+ artifactId: "deck-1",
29
+ };
30
+
31
+ describe("generation artifact access capabilities", () => {
32
+ beforeEach(() => {
33
+ vi.clearAllMocks();
34
+ process.env.CREATIVE_CONTEXT_A2A_KEY = "test-capability-signing-key";
35
+ mocks.getRequestUserEmail.mockReturnValue("alice@example.test");
36
+ mocks.getRequestOrgId.mockReturnValue("org-1");
37
+ mocks.assertAccess.mockResolvedValue({ role: "editor" });
38
+ });
39
+
40
+ afterEach(() => {
41
+ if (originalKey === undefined) delete process.env.CREATIVE_CONTEXT_A2A_KEY;
42
+ else process.env.CREATIVE_CONTEXT_A2A_KEY = originalKey;
43
+ });
44
+
45
+ it("mints a short-lived capability only after host artifact access succeeds", async () => {
46
+ const token = await createGenerationArtifactAccessCapability(
47
+ identity,
48
+ { resourceType: "deck", resourceId: "deck-1" },
49
+ "record",
50
+ );
51
+ expect(mocks.assertAccess).toHaveBeenCalledWith(
52
+ "deck",
53
+ "deck-1",
54
+ "editor",
55
+ undefined,
56
+ { skipResourceBody: true },
57
+ );
58
+
59
+ const proof = await verifyGenerationArtifactAccessCapability(
60
+ token,
61
+ identity,
62
+ "record",
63
+ );
64
+ expect(() =>
65
+ assertGenerationArtifactAccessProof(identity, proof, "editor"),
66
+ ).not.toThrow();
67
+ });
68
+
69
+ it("binds the capability to caller, artifact identity, and operation", async () => {
70
+ const token = await createGenerationArtifactAccessCapability(
71
+ identity,
72
+ { resourceType: "deck", resourceId: "deck-1" },
73
+ "read",
74
+ );
75
+
76
+ await expect(
77
+ verifyGenerationArtifactAccessCapability(
78
+ token,
79
+ { ...identity, artifactId: "guessed-deck" },
80
+ "read",
81
+ ),
82
+ ).rejects.toThrow(/invalid generation artifact access capability/i);
83
+ await expect(
84
+ verifyGenerationArtifactAccessCapability(token, identity, "record"),
85
+ ).rejects.toThrow(/invalid generation artifact access capability/i);
86
+ mocks.getRequestUserEmail.mockReturnValue("mallory@example.test");
87
+ await expect(
88
+ verifyGenerationArtifactAccessCapability(token, identity, "read"),
89
+ ).rejects.toThrow(/invalid generation artifact access capability/i);
90
+ });
91
+
92
+ it("rejects forged capability bytes", async () => {
93
+ const token = await createGenerationArtifactAccessCapability(
94
+ identity,
95
+ { resourceType: "deck", resourceId: "deck-1" },
96
+ "read",
97
+ );
98
+ const forged = `${token.slice(0, -1)}${token.endsWith("a") ? "b" : "a"}`;
99
+
100
+ await expect(
101
+ verifyGenerationArtifactAccessCapability(forged, identity, "read"),
102
+ ).rejects.toThrow(/invalid generation artifact access capability/i);
103
+ });
104
+ });
@@ -10,12 +10,18 @@ const mocks = vi.hoisted(() => ({
10
10
  getLocal: vi.fn(),
11
11
  hasA2A: vi.fn(),
12
12
  callA2A: vi.fn(),
13
+ getRequestOrgId: vi.fn(),
14
+ createArtifactCapability: vi.fn(),
13
15
  }));
14
16
 
15
17
  vi.mock("@agent-native/core/application-state", () => ({
16
18
  readAppState: mocks.readAppState,
17
19
  }));
18
20
 
21
+ vi.mock("@agent-native/core/server/request-context", () => ({
22
+ getRequestOrgId: mocks.getRequestOrgId,
23
+ }));
24
+
19
25
  vi.mock("../store/index.js", () => ({
20
26
  getContextPack: mocks.getContextPack,
21
27
  getCreativeContextItem: mocks.getCreativeContextItem,
@@ -43,6 +49,11 @@ vi.mock("./isolated-a2a.js", () => ({
43
49
  })),
44
50
  }));
45
51
 
52
+ vi.mock("./generation-artifact-access.js", () => ({
53
+ assertGenerationArtifactAccess: vi.fn(),
54
+ createGenerationArtifactAccessCapability: mocks.createArtifactCapability,
55
+ }));
56
+
46
57
  import {
47
58
  getGenerationCreativeContext,
48
59
  recordGenerationCreativeContext,
@@ -62,6 +73,10 @@ describe("generation context isolated A2A routing", () => {
62
73
  vi.clearAllMocks();
63
74
  mocks.readAppState.mockResolvedValue({ contextMode: "auto" });
64
75
  mocks.hasA2A.mockReturnValue(true);
76
+ mocks.getRequestOrgId.mockReturnValue("org-1");
77
+ mocks.createArtifactCapability.mockImplementation(
78
+ async (_identity, _target, operation) => `cap-${operation}`,
79
+ );
65
80
  mocks.callA2A.mockResolvedValue(emptyRemoteContext);
66
81
  });
67
82
 
@@ -104,12 +119,15 @@ describe("generation context isolated A2A routing", () => {
104
119
  artifactType: "deck",
105
120
  artifactId: "deck-1",
106
121
  },
107
- accessScope: "owner",
122
+ artifactAccessCapability: "cap-read",
108
123
  });
109
124
  expect(mocks.callA2A).toHaveBeenNthCalledWith(
110
125
  4,
111
126
  "record",
112
- expect.objectContaining({ artifactId: "deck-1" }),
127
+ expect.objectContaining({
128
+ artifactId: "deck-1",
129
+ artifactAccessCapability: "cap-record",
130
+ }),
113
131
  );
114
132
  expect(mocks.recordLocal).not.toHaveBeenCalled();
115
133
  expect(mocks.getLocal).not.toHaveBeenCalled();
@@ -14,18 +14,18 @@ import type {
14
14
  CreativeContextElementProvenance,
15
15
  CreativeContextReuseLabel,
16
16
  } from "../types.js";
17
- import {
18
- callIsolatedCreativeContextA2A,
19
- hasIsolatedCreativeContextA2A,
20
- isolatedResolvePayload,
21
- type IsolatedRecordPayload,
22
- } from "./isolated-a2a.js";
23
17
  import {
24
18
  assertGenerationArtifactAccess,
25
19
  createGenerationArtifactAccessCapability,
26
20
  type GenerationArtifactAccessTarget,
27
21
  type GenerationArtifactIdentity,
28
22
  } from "./generation-artifact-access.js";
23
+ import {
24
+ callIsolatedCreativeContextA2A,
25
+ hasIsolatedCreativeContextA2A,
26
+ isolatedResolvePayload,
27
+ type IsolatedRecordPayload,
28
+ } from "./isolated-a2a.js";
29
29
  import { performCreativeContextSearch } from "./retrieval.js";
30
30
  import {
31
31
  sanitizeUntrustedReference,
@@ -141,7 +141,6 @@ describe("isolated Creative Context A2A", () => {
141
141
  artifactType: "deck",
142
142
  artifactId: "deck-1",
143
143
  },
144
- accessScope: "owner",
145
144
  },
146
145
  {
147
146
  callAgent: vi.fn(async () => {
@@ -154,6 +153,27 @@ describe("isolated Creative Context A2A", () => {
154
153
  );
155
154
  });
156
155
 
156
+ it("rejects the retired caller-selectable collaborative read scope", () => {
157
+ const requestToken = Buffer.from(
158
+ JSON.stringify({
159
+ protocol: "creative-context-a2a-v1",
160
+ requestId: "87f466ae-32f4-4d0f-9de7-96f955e69f7b",
161
+ operation: "read",
162
+ payload: {
163
+ identity: {
164
+ appId: "slides",
165
+ artifactType: "deck",
166
+ artifactId: "guessed-deck",
167
+ },
168
+ accessScope: "artifact-access-asserted",
169
+ },
170
+ }),
171
+ "utf8",
172
+ ).toString("base64url");
173
+
174
+ expect(() => decodeCreativeContextA2ARequest(requestToken)).toThrow();
175
+ });
176
+
157
177
  it("fails closed when the isolated URL has no signing secret", async () => {
158
178
  delete process.env.CREATIVE_CONTEXT_A2A_KEY;
159
179
  delete process.env.A2A_SECRET;