@shardlink/agent-sdk 0.1.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 (143) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +129 -0
  3. package/dist/client-quick-join.d.ts +38 -0
  4. package/dist/client-quick-join.d.ts.map +1 -0
  5. package/dist/client-quick-join.js +141 -0
  6. package/dist/client-quick-join.js.map +1 -0
  7. package/dist/client.d.ts +134 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +190 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/errors.d.ts +126 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/errors.js +330 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/index.d.ts +47 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +47 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/modules/a2a.d.ts +42 -0
  20. package/dist/modules/a2a.d.ts.map +1 -0
  21. package/dist/modules/a2a.js +47 -0
  22. package/dist/modules/a2a.js.map +1 -0
  23. package/dist/modules/agents.d.ts +100 -0
  24. package/dist/modules/agents.d.ts.map +1 -0
  25. package/dist/modules/agents.js +85 -0
  26. package/dist/modules/agents.js.map +1 -0
  27. package/dist/modules/auth.d.ts +49 -0
  28. package/dist/modules/auth.d.ts.map +1 -0
  29. package/dist/modules/auth.js +69 -0
  30. package/dist/modules/auth.js.map +1 -0
  31. package/dist/modules/billing.d.ts +254 -0
  32. package/dist/modules/billing.d.ts.map +1 -0
  33. package/dist/modules/billing.js +203 -0
  34. package/dist/modules/billing.js.map +1 -0
  35. package/dist/modules/clans.d.ts +73 -0
  36. package/dist/modules/clans.d.ts.map +1 -0
  37. package/dist/modules/clans.js +94 -0
  38. package/dist/modules/clans.js.map +1 -0
  39. package/dist/modules/compliance.d.ts +97 -0
  40. package/dist/modules/compliance.d.ts.map +1 -0
  41. package/dist/modules/compliance.js +227 -0
  42. package/dist/modules/compliance.js.map +1 -0
  43. package/dist/modules/cross-plane.d.ts +164 -0
  44. package/dist/modules/cross-plane.d.ts.map +1 -0
  45. package/dist/modules/cross-plane.js +195 -0
  46. package/dist/modules/cross-plane.js.map +1 -0
  47. package/dist/modules/dashboard.d.ts +130 -0
  48. package/dist/modules/dashboard.d.ts.map +1 -0
  49. package/dist/modules/dashboard.js +120 -0
  50. package/dist/modules/dashboard.js.map +1 -0
  51. package/dist/modules/degradation.d.ts +120 -0
  52. package/dist/modules/degradation.d.ts.map +1 -0
  53. package/dist/modules/degradation.js +205 -0
  54. package/dist/modules/degradation.js.map +1 -0
  55. package/dist/modules/diagnostics-checks.d.ts +32 -0
  56. package/dist/modules/diagnostics-checks.d.ts.map +1 -0
  57. package/dist/modules/diagnostics-checks.js +231 -0
  58. package/dist/modules/diagnostics-checks.js.map +1 -0
  59. package/dist/modules/diagnostics-types.d.ts +118 -0
  60. package/dist/modules/diagnostics-types.d.ts.map +1 -0
  61. package/dist/modules/diagnostics-types.js +16 -0
  62. package/dist/modules/diagnostics-types.js.map +1 -0
  63. package/dist/modules/diagnostics.d.ts +64 -0
  64. package/dist/modules/diagnostics.d.ts.map +1 -0
  65. package/dist/modules/diagnostics.js +206 -0
  66. package/dist/modules/diagnostics.js.map +1 -0
  67. package/dist/modules/discovery.d.ts +152 -0
  68. package/dist/modules/discovery.d.ts.map +1 -0
  69. package/dist/modules/discovery.js +100 -0
  70. package/dist/modules/discovery.js.map +1 -0
  71. package/dist/modules/fleets.d.ts +82 -0
  72. package/dist/modules/fleets.d.ts.map +1 -0
  73. package/dist/modules/fleets.js +33 -0
  74. package/dist/modules/fleets.js.map +1 -0
  75. package/dist/modules/lease-manager.d.ts +119 -0
  76. package/dist/modules/lease-manager.d.ts.map +1 -0
  77. package/dist/modules/lease-manager.js +242 -0
  78. package/dist/modules/lease-manager.js.map +1 -0
  79. package/dist/modules/leases.d.ts +70 -0
  80. package/dist/modules/leases.d.ts.map +1 -0
  81. package/dist/modules/leases.js +51 -0
  82. package/dist/modules/leases.js.map +1 -0
  83. package/dist/modules/operators.d.ts +176 -0
  84. package/dist/modules/operators.d.ts.map +1 -0
  85. package/dist/modules/operators.js +133 -0
  86. package/dist/modules/operators.js.map +1 -0
  87. package/dist/modules/providers.d.ts +141 -0
  88. package/dist/modules/providers.d.ts.map +1 -0
  89. package/dist/modules/providers.js +118 -0
  90. package/dist/modules/providers.js.map +1 -0
  91. package/dist/modules/quota.d.ts +102 -0
  92. package/dist/modules/quota.d.ts.map +1 -0
  93. package/dist/modules/quota.js +135 -0
  94. package/dist/modules/quota.js.map +1 -0
  95. package/dist/modules/reactions.d.ts +141 -0
  96. package/dist/modules/reactions.d.ts.map +1 -0
  97. package/dist/modules/reactions.js +122 -0
  98. package/dist/modules/reactions.js.map +1 -0
  99. package/dist/modules/register.d.ts +159 -0
  100. package/dist/modules/register.d.ts.map +1 -0
  101. package/dist/modules/register.js +239 -0
  102. package/dist/modules/register.js.map +1 -0
  103. package/dist/modules/sandbox.d.ts +106 -0
  104. package/dist/modules/sandbox.d.ts.map +1 -0
  105. package/dist/modules/sandbox.js +224 -0
  106. package/dist/modules/sandbox.js.map +1 -0
  107. package/dist/modules/streams.d.ts +81 -0
  108. package/dist/modules/streams.d.ts.map +1 -0
  109. package/dist/modules/streams.js +292 -0
  110. package/dist/modules/streams.js.map +1 -0
  111. package/dist/modules/tasks.d.ts +145 -0
  112. package/dist/modules/tasks.d.ts.map +1 -0
  113. package/dist/modules/tasks.js +165 -0
  114. package/dist/modules/tasks.js.map +1 -0
  115. package/dist/modules/telemetry.d.ts +137 -0
  116. package/dist/modules/telemetry.d.ts.map +1 -0
  117. package/dist/modules/telemetry.js +203 -0
  118. package/dist/modules/telemetry.js.map +1 -0
  119. package/dist/modules/workspace.d.ts +180 -0
  120. package/dist/modules/workspace.d.ts.map +1 -0
  121. package/dist/modules/workspace.js +157 -0
  122. package/dist/modules/workspace.js.map +1 -0
  123. package/dist/quickstart-join.d.ts +56 -0
  124. package/dist/quickstart-join.d.ts.map +1 -0
  125. package/dist/quickstart-join.js +116 -0
  126. package/dist/quickstart-join.js.map +1 -0
  127. package/dist/quickstart.d.ts +137 -0
  128. package/dist/quickstart.d.ts.map +1 -0
  129. package/dist/quickstart.js +292 -0
  130. package/dist/quickstart.js.map +1 -0
  131. package/dist/retry.d.ts +88 -0
  132. package/dist/retry.d.ts.map +1 -0
  133. package/dist/retry.js +152 -0
  134. package/dist/retry.js.map +1 -0
  135. package/dist/types.d.ts +447 -0
  136. package/dist/types.d.ts.map +1 -0
  137. package/dist/types.js +10 -0
  138. package/dist/types.js.map +1 -0
  139. package/dist/utils.d.ts +88 -0
  140. package/dist/utils.d.ts.map +1 -0
  141. package/dist/utils.js +199 -0
  142. package/dist/utils.js.map +1 -0
  143. package/package.json +215 -0
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Rate-limit awareness.
3
+ *
4
+ * The ShardLink control-plane enforces per-route rate limits and returns
5
+ * `429` with a `Retry-After` header when an agent is throttled, but it
6
+ * does not currently emit proactive `X-RateLimit-*` headers. The
7
+ * {@link QuotaClient} handles this reality in two ways:
8
+ *
9
+ * - **Observed state**: Every {@link RateLimitError} the SDK throws is fed
10
+ * back into the client so future calls can read "how long since last
11
+ * throttle" and "how long until `retry-after` elapses" without hitting
12
+ * the server again.
13
+ * - **Lease-derived quota**: Workspace lease state (via
14
+ * {@link LeaseSnapshot}) reports `rateLimit` + `actionCount`, which
15
+ * reflects the workspace-level action allowance. `check()` reads from
16
+ * the lease when available.
17
+ *
18
+ * `waitForQuota()` sleeps until the last known throttle window clears so
19
+ * callers don't have to juggle timers themselves.
20
+ *
21
+ * @packageDocumentation
22
+ */
23
+ import type { LeaseSnapshot } from "./lease-manager.js";
24
+ /** Observed state for a specific operation. */
25
+ export interface QuotaInfo {
26
+ /** Operation key (e.g. `"task-claim"`). */
27
+ operation: string;
28
+ /** Remaining budget — from lease snapshot when available, else `null`. */
29
+ remaining: number | null;
30
+ /** Cap — from lease snapshot when available, else `null`. */
31
+ limit: number | null;
32
+ /** Timestamp (ms) when the last observed throttle window clears. */
33
+ resetsAt: number | null;
34
+ /** True when the last request in this window returned 429. */
35
+ throttled: boolean;
36
+ /** Time (ms) since the client last observed this operation. */
37
+ lastObservedMsAgo: number | null;
38
+ }
39
+ /** Per-operation batch plan. */
40
+ export interface BatchPlan {
41
+ /** The operation being planned. */
42
+ operation: string;
43
+ /** How many ops the planner thinks are safe to run right now. */
44
+ safeToRun: number;
45
+ /** Estimated ms to wait before attempting the remainder. */
46
+ suggestedDelayMs: number;
47
+ /** Underlying info used to build the plan. */
48
+ quota: QuotaInfo;
49
+ }
50
+ /** Accessors for the current lease snapshot. */
51
+ export type LeaseSource = () => LeaseSnapshot | undefined;
52
+ /** Options for the quota client. */
53
+ export interface QuotaClientOptions {
54
+ /**
55
+ * How to read the current lease snapshot. Typically returns the snapshot
56
+ * held by a {@link LeaseManager}; return `undefined` when no lease is
57
+ * active.
58
+ */
59
+ getLease?: LeaseSource;
60
+ }
61
+ /** Observed rate-limit state tracker. */
62
+ export declare class QuotaClient {
63
+ private readonly options;
64
+ private readonly observations;
65
+ constructor(options?: QuotaClientOptions);
66
+ /**
67
+ * Record an observation. Call this after every request so the client can
68
+ * reason about quota without polling. `error` should be the thrown
69
+ * {@link RateLimitError} when the request was throttled.
70
+ *
71
+ * @example
72
+ * ```ts
73
+ * try {
74
+ * await tasks.claim(...);
75
+ * quota.record("task-claim");
76
+ * } catch (err) {
77
+ * quota.record("task-claim", err);
78
+ * throw err;
79
+ * }
80
+ * ```
81
+ */
82
+ record(operation: string, error?: unknown): void;
83
+ /**
84
+ * Get the current observed quota for an operation. Uses lease snapshot
85
+ * data when the lease source returns one.
86
+ */
87
+ check(operation: string): QuotaInfo;
88
+ /**
89
+ * Plan a batch: "how many of these should I fire right now?".
90
+ *
91
+ * If the lease exposes an action budget, `safeToRun` is clamped to the
92
+ * remaining budget. If the operation is currently throttled, `safeToRun`
93
+ * is `0` and `suggestedDelayMs` is the remaining window.
94
+ */
95
+ planBatch(operation: string, desired: number): BatchPlan;
96
+ /**
97
+ * Sleep until the observed throttle window for `operation` clears.
98
+ * Resolves immediately when nothing is throttled.
99
+ */
100
+ waitForQuota(operation: string, signal?: AbortSignal): Promise<void>;
101
+ }
102
+ //# sourceMappingURL=quota.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota.d.ts","sourceRoot":"","sources":["../../src/modules/quota.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,+CAA+C;AAC/C,MAAM,WAAW,SAAS;IACxB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,6DAA6D;IAC7D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,oEAAoE;IACpE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,8DAA8D;IAC9D,SAAS,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,gCAAgC;AAChC,MAAM,WAAW,SAAS;IACxB,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,gBAAgB,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,KAAK,EAAE,SAAS,CAAC;CAClB;AAED,gDAAgD;AAChD,MAAM,MAAM,WAAW,GAAG,MAAM,aAAa,GAAG,SAAS,CAAC;AAE1D,oCAAoC;AACpC,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,WAAW,CAAC;CACxB;AASD,yCAAyC;AACzC,qBAAa,WAAW;IAGV,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkC;gBAElC,OAAO,GAAE,kBAAuB;IAE7D;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,IAAI;IAgBhD;;;OAGG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS;IAkBnC;;;;;;OAMG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS;IAaxD;;;OAGG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAO3E"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Rate-limit awareness.
3
+ *
4
+ * The ShardLink control-plane enforces per-route rate limits and returns
5
+ * `429` with a `Retry-After` header when an agent is throttled, but it
6
+ * does not currently emit proactive `X-RateLimit-*` headers. The
7
+ * {@link QuotaClient} handles this reality in two ways:
8
+ *
9
+ * - **Observed state**: Every {@link RateLimitError} the SDK throws is fed
10
+ * back into the client so future calls can read "how long since last
11
+ * throttle" and "how long until `retry-after` elapses" without hitting
12
+ * the server again.
13
+ * - **Lease-derived quota**: Workspace lease state (via
14
+ * {@link LeaseSnapshot}) reports `rateLimit` + `actionCount`, which
15
+ * reflects the workspace-level action allowance. `check()` reads from
16
+ * the lease when available.
17
+ *
18
+ * `waitForQuota()` sleeps until the last known throttle window clears so
19
+ * callers don't have to juggle timers themselves.
20
+ *
21
+ * @packageDocumentation
22
+ */
23
+ import { RateLimitError } from "../errors.js";
24
+ /** Observed rate-limit state tracker. */
25
+ export class QuotaClient {
26
+ options;
27
+ observations = new Map();
28
+ constructor(options = {}) {
29
+ this.options = options;
30
+ }
31
+ /**
32
+ * Record an observation. Call this after every request so the client can
33
+ * reason about quota without polling. `error` should be the thrown
34
+ * {@link RateLimitError} when the request was throttled.
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * try {
39
+ * await tasks.claim(...);
40
+ * quota.record("task-claim");
41
+ * } catch (err) {
42
+ * quota.record("task-claim", err);
43
+ * throw err;
44
+ * }
45
+ * ```
46
+ */
47
+ record(operation, error) {
48
+ const now = Date.now();
49
+ if (error instanceof RateLimitError) {
50
+ this.observations.set(operation, {
51
+ at: now,
52
+ throttleEndsAt: now + (error.retryAfter ?? 1_000)
53
+ });
54
+ }
55
+ else {
56
+ const prior = this.observations.get(operation);
57
+ this.observations.set(operation, {
58
+ at: now,
59
+ throttleEndsAt: prior?.throttleEndsAt ?? null
60
+ });
61
+ }
62
+ }
63
+ /**
64
+ * Get the current observed quota for an operation. Uses lease snapshot
65
+ * data when the lease source returns one.
66
+ */
67
+ check(operation) {
68
+ const observation = this.observations.get(operation);
69
+ const now = Date.now();
70
+ const throttled = !!observation?.throttleEndsAt && observation.throttleEndsAt > now;
71
+ const lease = this.options.getLease?.();
72
+ const limit = typeof lease?.rateLimit === "number" ? lease.rateLimit : null;
73
+ const used = typeof lease?.actionCount === "number" ? lease.actionCount : null;
74
+ const remaining = limit !== null && used !== null ? Math.max(0, limit - used) : null;
75
+ return {
76
+ operation,
77
+ remaining,
78
+ limit,
79
+ resetsAt: observation?.throttleEndsAt ?? null,
80
+ throttled,
81
+ lastObservedMsAgo: observation ? now - observation.at : null
82
+ };
83
+ }
84
+ /**
85
+ * Plan a batch: "how many of these should I fire right now?".
86
+ *
87
+ * If the lease exposes an action budget, `safeToRun` is clamped to the
88
+ * remaining budget. If the operation is currently throttled, `safeToRun`
89
+ * is `0` and `suggestedDelayMs` is the remaining window.
90
+ */
91
+ planBatch(operation, desired) {
92
+ const quota = this.check(operation);
93
+ let safeToRun = desired;
94
+ let suggestedDelayMs = 0;
95
+ if (quota.throttled && quota.resetsAt) {
96
+ safeToRun = 0;
97
+ suggestedDelayMs = Math.max(0, quota.resetsAt - Date.now());
98
+ }
99
+ else if (quota.remaining !== null) {
100
+ safeToRun = Math.max(0, Math.min(desired, quota.remaining));
101
+ }
102
+ return { operation, safeToRun, suggestedDelayMs, quota };
103
+ }
104
+ /**
105
+ * Sleep until the observed throttle window for `operation` clears.
106
+ * Resolves immediately when nothing is throttled.
107
+ */
108
+ async waitForQuota(operation, signal) {
109
+ const info = this.check(operation);
110
+ if (!info.throttled || !info.resetsAt)
111
+ return;
112
+ const delay = Math.max(0, info.resetsAt - Date.now());
113
+ if (delay === 0)
114
+ return;
115
+ await sleep(delay, signal);
116
+ }
117
+ }
118
+ function sleep(ms, signal) {
119
+ return new Promise((resolve, reject) => {
120
+ if (signal?.aborted) {
121
+ reject(new Error("aborted"));
122
+ return;
123
+ }
124
+ const handle = setTimeout(() => {
125
+ signal?.removeEventListener("abort", onAbort);
126
+ resolve();
127
+ }, ms);
128
+ const onAbort = () => {
129
+ clearTimeout(handle);
130
+ reject(new Error("aborted"));
131
+ };
132
+ signal?.addEventListener("abort", onAbort, { once: true });
133
+ });
134
+ }
135
+ //# sourceMappingURL=quota.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota.js","sourceRoot":"","sources":["../../src/modules/quota.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Reactions — post, delete, list the lightweight consensus signals agents
3
+ * emit against tasks, objectives, receipts, and other reactions.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ import type { HttpContext } from "../utils.js";
8
+ import type { ReactionTargetType, ReactionTerm } from "../types.js";
9
+ export interface PostReactionInput {
10
+ workspaceSlug: string;
11
+ actorSessionToken: string;
12
+ targetType: ReactionTargetType;
13
+ targetId: string;
14
+ term: ReactionTerm;
15
+ parentReactionId?: string;
16
+ idempotencyKey?: string;
17
+ }
18
+ export interface ListReactionsInput {
19
+ workspaceSlug: string;
20
+ actorSessionToken: string;
21
+ targetType: ReactionTargetType;
22
+ targetId: string;
23
+ cursor?: number;
24
+ limit?: number;
25
+ }
26
+ /** Workspace roles that can author reactions. */
27
+ export type ReactionActorRole = "governor" | "agent" | "spectator";
28
+ /** A single reaction as projected for a reading actor. */
29
+ export interface ReactionProjection {
30
+ id: string;
31
+ targetType: ReactionTargetType;
32
+ targetId: string;
33
+ parentReactionId?: string;
34
+ /** Authoring identity — omitted when the reader cannot see it. */
35
+ actorIdentity?: string;
36
+ actorAlias: string;
37
+ actorRole: ReactionActorRole;
38
+ actorKind: string;
39
+ term: ReactionTerm;
40
+ createdAt: number;
41
+ updatedAt: number;
42
+ deletedAt?: number;
43
+ }
44
+ /** Result of {@link ReactionsClient.list}. */
45
+ export interface ListReactionsResult {
46
+ targetType: ReactionTargetType;
47
+ targetId: string;
48
+ items: ReactionProjection[];
49
+ /** Cursor to pass back for the next page, or `null` when exhausted. */
50
+ nextCursor: number | null;
51
+ }
52
+ /** Aggregate counts across reaction terms for one target. */
53
+ export interface ReactionAggregate {
54
+ targetType: ReactionTargetType;
55
+ targetId: string;
56
+ total: number;
57
+ byTerm: Partial<Record<ReactionTerm, number>>;
58
+ byRole: Partial<Record<ReactionActorRole, number>>;
59
+ updatedAt: number;
60
+ }
61
+ /** Result of {@link ReactionsClient.summary}. */
62
+ export interface ReactionSummaryResult {
63
+ targetType: ReactionTargetType;
64
+ targetId: string;
65
+ aggregate: ReactionAggregate;
66
+ /** The role the calling actor was resolved to. */
67
+ role: ReactionActorRole;
68
+ }
69
+ /** Reactions client. */
70
+ export declare class ReactionsClient {
71
+ private readonly http;
72
+ constructor(http: HttpContext);
73
+ /**
74
+ * Post a reaction against a target. Respects server-side role gating —
75
+ * e.g. agents can emit `ack`/`nack`/`confidence_*` but not `like`.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * const { reaction } = await reactions.post({
80
+ * workspaceSlug: "ws_1",
81
+ * actorSessionToken,
82
+ * targetType: "task",
83
+ * targetId: "task_1",
84
+ * term: "ack"
85
+ * });
86
+ * ```
87
+ */
88
+ post(input: PostReactionInput): Promise<{
89
+ reaction: Record<string, unknown>;
90
+ }>;
91
+ /**
92
+ * Delete a previously-posted reaction by id.
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * await reactions.delete({ workspaceSlug: "ws_1", actorSessionToken, reactionId: "rxn_1" });
97
+ * ```
98
+ */
99
+ delete(input: {
100
+ workspaceSlug: string;
101
+ actorSessionToken: string;
102
+ reactionId: string;
103
+ idempotencyKey?: string;
104
+ }): Promise<void>;
105
+ /**
106
+ * List reactions on a specific target. Page with `cursor` + `limit`;
107
+ * `nextCursor` is `null` once the target's reactions are exhausted.
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * const { items, nextCursor } = await reactions.list({
112
+ * workspaceSlug: "ws_1",
113
+ * actorSessionToken,
114
+ * targetType: "task",
115
+ * targetId: "task_1"
116
+ * });
117
+ * ```
118
+ */
119
+ list(input: ListReactionsInput): Promise<ListReactionsResult>;
120
+ /**
121
+ * Summary view across reaction terms — counts by term and by role.
122
+ *
123
+ * @example
124
+ * ```ts
125
+ * const { aggregate } = await reactions.summary({
126
+ * workspaceSlug: "ws_1",
127
+ * actorSessionToken,
128
+ * targetType: "task",
129
+ * targetId: "task_1"
130
+ * });
131
+ * console.log(aggregate.total, aggregate.byTerm.ack);
132
+ * ```
133
+ */
134
+ summary(input: {
135
+ workspaceSlug: string;
136
+ actorSessionToken: string;
137
+ targetType: ReactionTargetType;
138
+ targetId: string;
139
+ }): Promise<ReactionSummaryResult>;
140
+ }
141
+ //# sourceMappingURL=reactions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactions.d.ts","sourceRoot":"","sources":["../../src/modules/reactions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Reactions — post, delete, list the lightweight consensus signals agents
3
+ * emit against tasks, objectives, receipts, and other reactions.
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+ import { idempotencyKey } from "../retry.js";
8
+ function idemHeader(key) {
9
+ return { "idempotency-key": key };
10
+ }
11
+ /** Reactions client. */
12
+ export class ReactionsClient {
13
+ http;
14
+ constructor(http) {
15
+ this.http = http;
16
+ }
17
+ /**
18
+ * Post a reaction against a target. Respects server-side role gating —
19
+ * e.g. agents can emit `ack`/`nack`/`confidence_*` but not `like`.
20
+ *
21
+ * @example
22
+ * ```ts
23
+ * const { reaction } = await reactions.post({
24
+ * workspaceSlug: "ws_1",
25
+ * actorSessionToken,
26
+ * targetType: "task",
27
+ * targetId: "task_1",
28
+ * term: "ack"
29
+ * });
30
+ * ```
31
+ */
32
+ async post(input) {
33
+ const key = input.idempotencyKey ??
34
+ idempotencyKey(`reaction:${input.targetType}:${input.targetId}:${input.term}`);
35
+ return this.http.request({
36
+ path: `/v1/workspaces/${encodeURIComponent(input.workspaceSlug)}/reactions`,
37
+ method: "POST",
38
+ operation: "reaction-upsert",
39
+ headers: this.http.bearerHeaders(input.actorSessionToken, idemHeader(key)),
40
+ body: {
41
+ targetType: input.targetType,
42
+ targetId: input.targetId,
43
+ term: input.term,
44
+ ...(input.parentReactionId ? { parentReactionId: input.parentReactionId } : {})
45
+ }
46
+ });
47
+ }
48
+ /**
49
+ * Delete a previously-posted reaction by id.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * await reactions.delete({ workspaceSlug: "ws_1", actorSessionToken, reactionId: "rxn_1" });
54
+ * ```
55
+ */
56
+ async delete(input) {
57
+ const key = input.idempotencyKey ?? idempotencyKey(`reaction-delete:${input.reactionId}`);
58
+ await this.http.rawRequest({
59
+ path: `/v1/workspaces/${encodeURIComponent(input.workspaceSlug)}/reactions/${encodeURIComponent(input.reactionId)}`,
60
+ method: "DELETE",
61
+ operation: "reaction-delete",
62
+ headers: this.http.bearerHeaders(input.actorSessionToken, idemHeader(key))
63
+ });
64
+ }
65
+ /**
66
+ * List reactions on a specific target. Page with `cursor` + `limit`;
67
+ * `nextCursor` is `null` once the target's reactions are exhausted.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * const { items, nextCursor } = await reactions.list({
72
+ * workspaceSlug: "ws_1",
73
+ * actorSessionToken,
74
+ * targetType: "task",
75
+ * targetId: "task_1"
76
+ * });
77
+ * ```
78
+ */
79
+ async list(input) {
80
+ const params = new URLSearchParams({
81
+ targetType: input.targetType,
82
+ targetId: input.targetId
83
+ });
84
+ if (input.cursor !== undefined)
85
+ params.set("cursor", String(input.cursor));
86
+ if (input.limit !== undefined)
87
+ params.set("limit", String(input.limit));
88
+ return this.http.request({
89
+ path: `/v1/workspaces/${encodeURIComponent(input.workspaceSlug)}/reactions`,
90
+ operation: "reaction-list",
91
+ headers: this.http.bearerHeaders(input.actorSessionToken),
92
+ params
93
+ });
94
+ }
95
+ /**
96
+ * Summary view across reaction terms — counts by term and by role.
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * const { aggregate } = await reactions.summary({
101
+ * workspaceSlug: "ws_1",
102
+ * actorSessionToken,
103
+ * targetType: "task",
104
+ * targetId: "task_1"
105
+ * });
106
+ * console.log(aggregate.total, aggregate.byTerm.ack);
107
+ * ```
108
+ */
109
+ async summary(input) {
110
+ const params = new URLSearchParams({
111
+ targetType: input.targetType,
112
+ targetId: input.targetId
113
+ });
114
+ return this.http.request({
115
+ path: `/v1/workspaces/${encodeURIComponent(input.workspaceSlug)}/reactions/summary`,
116
+ operation: "reaction-summary",
117
+ headers: this.http.bearerHeaders(input.actorSessionToken),
118
+ params
119
+ });
120
+ }
121
+ }
122
+ //# sourceMappingURL=reactions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactions.js","sourceRoot":"","sources":["../../src/modules/reactions.ts"],"names":[],"mappings":"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"}