agor-live 0.7.1 → 0.7.2

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.
@@ -50,9 +50,10 @@ var PermissionService = class {
50
50
  *
51
51
  * @param requestId - Unique permission request ID
52
52
  * @param taskId - Task waiting for permission (used for timeout/cancel fallback)
53
+ * @param sessionId - Session ID for tracking pending requests per session
53
54
  * @param signal - AbortSignal for cancellation
54
55
  */
55
- waitForDecision(requestId, taskId, signal) {
56
+ waitForDecision(requestId, taskId, sessionId, signal) {
56
57
  return new Promise((resolve) => {
57
58
  signal.addEventListener("abort", () => {
58
59
  const pending = this.pendingRequests.get(requestId);
@@ -86,7 +87,7 @@ var PermissionService = class {
86
87
  // System-initiated timeout
87
88
  });
88
89
  }, 6e4);
89
- this.pendingRequests.set(requestId, { resolve, timeout });
90
+ this.pendingRequests.set(requestId, { sessionId, resolve, timeout });
90
91
  });
91
92
  }
92
93
  /**
@@ -105,6 +106,37 @@ var PermissionService = class {
105
106
  console.warn(`\u26A0\uFE0F No pending request found for ${decision.requestId}`);
106
107
  }
107
108
  }
109
+ /**
110
+ * Cancel all pending permission requests for a session
111
+ * Used when a permission is denied and we need to stop task execution
112
+ *
113
+ * @param sessionId - Session to cancel pending requests for
114
+ */
115
+ cancelPendingRequests(sessionId) {
116
+ let cancelledCount = 0;
117
+ for (const [requestId, pending] of this.pendingRequests.entries()) {
118
+ if (pending.sessionId === sessionId) {
119
+ clearTimeout(pending.timeout);
120
+ pending.resolve({
121
+ requestId,
122
+ taskId: "",
123
+ // Will be ignored since we're cancelling
124
+ allow: false,
125
+ reason: "Cancelled due to previous permission denial",
126
+ remember: false,
127
+ scope: "once" /* ONCE */,
128
+ decidedBy: "system"
129
+ });
130
+ this.pendingRequests.delete(requestId);
131
+ cancelledCount++;
132
+ }
133
+ }
134
+ if (cancelledCount > 0) {
135
+ console.log(
136
+ `\u{1F6E1}\uFE0F Cancelled ${cancelledCount} pending permission request(s) for session ${sessionId.substring(0, 8)}`
137
+ );
138
+ }
139
+ }
108
140
  };
109
141
  // Annotate the CommonJS export names for ESM import in node:
110
142
  0 && (module.exports = {
@@ -69,13 +69,21 @@ declare class PermissionService {
69
69
  *
70
70
  * @param requestId - Unique permission request ID
71
71
  * @param taskId - Task waiting for permission (used for timeout/cancel fallback)
72
+ * @param sessionId - Session ID for tracking pending requests per session
72
73
  * @param signal - AbortSignal for cancellation
73
74
  */
74
- waitForDecision(requestId: string, taskId: TaskID, signal: AbortSignal): Promise<PermissionDecision>;
75
+ waitForDecision(requestId: string, taskId: TaskID, sessionId: SessionID, signal: AbortSignal): Promise<PermissionDecision>;
75
76
  /**
76
77
  * Resolve a pending permission request with a decision from the UI
77
78
  */
78
79
  resolvePermission(decision: PermissionDecision): void;
80
+ /**
81
+ * Cancel all pending permission requests for a session
82
+ * Used when a permission is denied and we need to stop task execution
83
+ *
84
+ * @param sessionId - Session to cancel pending requests for
85
+ */
86
+ cancelPendingRequests(sessionId: SessionID): void;
79
87
  }
80
88
 
81
89
  export { type PermissionDecision, type PermissionRequest, PermissionService };
@@ -69,13 +69,21 @@ declare class PermissionService {
69
69
  *
70
70
  * @param requestId - Unique permission request ID
71
71
  * @param taskId - Task waiting for permission (used for timeout/cancel fallback)
72
+ * @param sessionId - Session ID for tracking pending requests per session
72
73
  * @param signal - AbortSignal for cancellation
73
74
  */
74
- waitForDecision(requestId: string, taskId: TaskID, signal: AbortSignal): Promise<PermissionDecision>;
75
+ waitForDecision(requestId: string, taskId: TaskID, sessionId: SessionID, signal: AbortSignal): Promise<PermissionDecision>;
75
76
  /**
76
77
  * Resolve a pending permission request with a decision from the UI
77
78
  */
78
79
  resolvePermission(decision: PermissionDecision): void;
80
+ /**
81
+ * Cancel all pending permission requests for a session
82
+ * Used when a permission is denied and we need to stop task execution
83
+ *
84
+ * @param sessionId - Session to cancel pending requests for
85
+ */
86
+ cancelPendingRequests(sessionId: SessionID): void;
79
87
  }
80
88
 
81
89
  export { type PermissionDecision, type PermissionRequest, PermissionService };
@@ -24,9 +24,10 @@ var PermissionService = class {
24
24
  *
25
25
  * @param requestId - Unique permission request ID
26
26
  * @param taskId - Task waiting for permission (used for timeout/cancel fallback)
27
+ * @param sessionId - Session ID for tracking pending requests per session
27
28
  * @param signal - AbortSignal for cancellation
28
29
  */
29
- waitForDecision(requestId, taskId, signal) {
30
+ waitForDecision(requestId, taskId, sessionId, signal) {
30
31
  return new Promise((resolve) => {
31
32
  signal.addEventListener("abort", () => {
32
33
  const pending = this.pendingRequests.get(requestId);
@@ -60,7 +61,7 @@ var PermissionService = class {
60
61
  // System-initiated timeout
61
62
  });
62
63
  }, 6e4);
63
- this.pendingRequests.set(requestId, { resolve, timeout });
64
+ this.pendingRequests.set(requestId, { sessionId, resolve, timeout });
64
65
  });
65
66
  }
66
67
  /**
@@ -79,6 +80,37 @@ var PermissionService = class {
79
80
  console.warn(`\u26A0\uFE0F No pending request found for ${decision.requestId}`);
80
81
  }
81
82
  }
83
+ /**
84
+ * Cancel all pending permission requests for a session
85
+ * Used when a permission is denied and we need to stop task execution
86
+ *
87
+ * @param sessionId - Session to cancel pending requests for
88
+ */
89
+ cancelPendingRequests(sessionId) {
90
+ let cancelledCount = 0;
91
+ for (const [requestId, pending] of this.pendingRequests.entries()) {
92
+ if (pending.sessionId === sessionId) {
93
+ clearTimeout(pending.timeout);
94
+ pending.resolve({
95
+ requestId,
96
+ taskId: "",
97
+ // Will be ignored since we're cancelling
98
+ allow: false,
99
+ reason: "Cancelled due to previous permission denial",
100
+ remember: false,
101
+ scope: "once" /* ONCE */,
102
+ decidedBy: "system"
103
+ });
104
+ this.pendingRequests.delete(requestId);
105
+ cancelledCount++;
106
+ }
107
+ }
108
+ if (cancelledCount > 0) {
109
+ console.log(
110
+ `\u{1F6E1}\uFE0F Cancelled ${cancelledCount} pending permission request(s) for session ${sessionId.substring(0, 8)}`
111
+ );
112
+ }
113
+ }
82
114
  };
83
115
  export {
84
116
  PermissionService
@@ -408,7 +408,8 @@ var SDKMessageProcessor = class {
408
408
  enableTokenStreaming: options.enableTokenStreaming ?? true,
409
409
  idleTimeoutMs: options.idleTimeoutMs ?? 3e4,
410
410
  // 30s default
411
- contentBlockStack: []
411
+ contentBlockStack: [],
412
+ toolInputChunkCount: 0
412
413
  };
413
414
  }
414
415
  /**
@@ -516,13 +517,11 @@ var SDKMessageProcessor = class {
516
517
  const hasText = Array.isArray(content) && content.some((b) => b.type === "text");
517
518
  if (hasToolResult) {
518
519
  const toolResults = content.filter((b) => b.type === "tool_result");
520
+ const errorCount = toolResults.filter((tr) => tr.is_error).length;
521
+ const successCount = toolResults.length - errorCount;
519
522
  console.log(
520
- `\u{1F527} SDK user message with ${toolResults.length} tool result(s) (uuid: ${uuid?.substring(0, 8)})`
523
+ `\u{1F527} SDK user message with ${toolResults.length} tool result(s) (\u2705 ${successCount}, \u274C ${errorCount})`
521
524
  );
522
- toolResults.forEach((tr, i) => {
523
- const preview = typeof tr.content === "string" ? tr.content.substring(0, 100) : JSON.stringify(tr.content).substring(0, 100);
524
- console.log(` Result ${i + 1}: ${tr.is_error ? "\u274C ERROR" : "\u2705"} ${preview}`);
525
- });
526
525
  return [
527
526
  {
528
527
  type: "complete",
@@ -630,9 +629,9 @@ var SDKMessageProcessor = class {
630
629
  agentSessionId: this.state.capturedAgentSessionId
631
630
  });
632
631
  } else if (delta?.type === "input_json_delta") {
633
- const partialJson = delta.partial_json;
634
- if (partialJson) {
635
- console.debug(`\u{1F527} Tool input chunk: ${partialJson.substring(0, 50)}...`);
632
+ this.state.toolInputChunkCount++;
633
+ if (this.state.toolInputChunkCount % 10 === 0) {
634
+ console.debug(`\u{1F527} Tool input chunk (${this.state.toolInputChunkCount})`);
636
635
  }
637
636
  }
638
637
  }
@@ -1598,6 +1597,7 @@ function createPreToolUseHook(sessionId, taskId, deps) {
1598
1597
  const decision = await deps.permissionService.waitForDecision(
1599
1598
  requestId,
1600
1599
  taskId,
1600
+ sessionId,
1601
1601
  options.signal
1602
1602
  );
1603
1603
  if (deps.messagesService) {
@@ -1618,11 +1618,23 @@ function createPreToolUseHook(sessionId, taskId, deps) {
1618
1618
  await deps.tasksService.patch(taskId, {
1619
1619
  status: decision.allow ? TaskStatus.RUNNING : TaskStatus.FAILED
1620
1620
  });
1621
- if (deps.sessionsService) {
1622
- await deps.sessionsService.patch(sessionId, {
1623
- status: "running"
1624
- });
1625
- console.log(`\u2705 Session ${sessionId} restored to running after permission decision`);
1621
+ if (!decision.allow) {
1622
+ console.log(`\u{1F6D1} Permission denied for ${input.tool_name}, stopping task execution...`);
1623
+ deps.permissionService.cancelPendingRequests(sessionId);
1624
+ if (deps.sessionsService) {
1625
+ await deps.sessionsService.patch(sessionId, {
1626
+ status: "idle"
1627
+ });
1628
+ console.log(`\u2705 Session ${sessionId} set to idle after permission denial`);
1629
+ }
1630
+ throw new Error(`Permission denied for tool: ${input.tool_name}`);
1631
+ } else {
1632
+ if (deps.sessionsService) {
1633
+ await deps.sessionsService.patch(sessionId, {
1634
+ status: "running"
1635
+ });
1636
+ console.log(`\u2705 Session ${sessionId} restored to running after permission approval`);
1637
+ }
1626
1638
  }
1627
1639
  if (decision.remember) {
1628
1640
  const freshSession = await deps.sessionsRepo.findById(sessionId);
@@ -1684,21 +1696,18 @@ function createPreToolUseHook(sessionId, taskId, deps) {
1684
1696
  try {
1685
1697
  const errorMessage = error instanceof Error ? error.message : String(error);
1686
1698
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1687
- await deps.tasksService.patch(taskId, {
1688
- status: TaskStatus.FAILED,
1689
- report: `Error: ${errorMessage}
1699
+ const isPermissionDenial = error instanceof Error && error.message.startsWith("Permission denied for tool:");
1700
+ if (!isPermissionDenial) {
1701
+ await deps.tasksService.patch(taskId, {
1702
+ status: TaskStatus.FAILED,
1703
+ report: `Error: ${errorMessage}
1690
1704
  Timestamp: ${timestamp}`
1691
- });
1705
+ });
1706
+ }
1692
1707
  } catch (updateError) {
1693
1708
  console.error("Failed to update task status:", updateError);
1694
1709
  }
1695
- return {
1696
- hookSpecificOutput: {
1697
- hookEventName: "PreToolUse",
1698
- permissionDecision: "deny",
1699
- permissionDecisionReason: `Permission hook failed: ${error instanceof Error ? error.message : String(error)}`
1700
- }
1701
- };
1710
+ throw error;
1702
1711
  } finally {
1703
1712
  if (releaseLock) {
1704
1713
  releaseLock();
@@ -350,7 +350,8 @@ var SDKMessageProcessor = class {
350
350
  enableTokenStreaming: options.enableTokenStreaming ?? true,
351
351
  idleTimeoutMs: options.idleTimeoutMs ?? 3e4,
352
352
  // 30s default
353
- contentBlockStack: []
353
+ contentBlockStack: [],
354
+ toolInputChunkCount: 0
354
355
  };
355
356
  }
356
357
  /**
@@ -458,13 +459,11 @@ var SDKMessageProcessor = class {
458
459
  const hasText = Array.isArray(content) && content.some((b) => b.type === "text");
459
460
  if (hasToolResult) {
460
461
  const toolResults = content.filter((b) => b.type === "tool_result");
462
+ const errorCount = toolResults.filter((tr) => tr.is_error).length;
463
+ const successCount = toolResults.length - errorCount;
461
464
  console.log(
462
- `\u{1F527} SDK user message with ${toolResults.length} tool result(s) (uuid: ${uuid?.substring(0, 8)})`
465
+ `\u{1F527} SDK user message with ${toolResults.length} tool result(s) (\u2705 ${successCount}, \u274C ${errorCount})`
463
466
  );
464
- toolResults.forEach((tr, i) => {
465
- const preview = typeof tr.content === "string" ? tr.content.substring(0, 100) : JSON.stringify(tr.content).substring(0, 100);
466
- console.log(` Result ${i + 1}: ${tr.is_error ? "\u274C ERROR" : "\u2705"} ${preview}`);
467
- });
468
467
  return [
469
468
  {
470
469
  type: "complete",
@@ -572,9 +571,9 @@ var SDKMessageProcessor = class {
572
571
  agentSessionId: this.state.capturedAgentSessionId
573
572
  });
574
573
  } else if (delta?.type === "input_json_delta") {
575
- const partialJson = delta.partial_json;
576
- if (partialJson) {
577
- console.debug(`\u{1F527} Tool input chunk: ${partialJson.substring(0, 50)}...`);
574
+ this.state.toolInputChunkCount++;
575
+ if (this.state.toolInputChunkCount % 10 === 0) {
576
+ console.debug(`\u{1F527} Tool input chunk (${this.state.toolInputChunkCount})`);
578
577
  }
579
578
  }
580
579
  }
@@ -1540,6 +1539,7 @@ function createPreToolUseHook(sessionId, taskId, deps) {
1540
1539
  const decision = await deps.permissionService.waitForDecision(
1541
1540
  requestId,
1542
1541
  taskId,
1542
+ sessionId,
1543
1543
  options.signal
1544
1544
  );
1545
1545
  if (deps.messagesService) {
@@ -1560,11 +1560,23 @@ function createPreToolUseHook(sessionId, taskId, deps) {
1560
1560
  await deps.tasksService.patch(taskId, {
1561
1561
  status: decision.allow ? TaskStatus.RUNNING : TaskStatus.FAILED
1562
1562
  });
1563
- if (deps.sessionsService) {
1564
- await deps.sessionsService.patch(sessionId, {
1565
- status: "running"
1566
- });
1567
- console.log(`\u2705 Session ${sessionId} restored to running after permission decision`);
1563
+ if (!decision.allow) {
1564
+ console.log(`\u{1F6D1} Permission denied for ${input.tool_name}, stopping task execution...`);
1565
+ deps.permissionService.cancelPendingRequests(sessionId);
1566
+ if (deps.sessionsService) {
1567
+ await deps.sessionsService.patch(sessionId, {
1568
+ status: "idle"
1569
+ });
1570
+ console.log(`\u2705 Session ${sessionId} set to idle after permission denial`);
1571
+ }
1572
+ throw new Error(`Permission denied for tool: ${input.tool_name}`);
1573
+ } else {
1574
+ if (deps.sessionsService) {
1575
+ await deps.sessionsService.patch(sessionId, {
1576
+ status: "running"
1577
+ });
1578
+ console.log(`\u2705 Session ${sessionId} restored to running after permission approval`);
1579
+ }
1568
1580
  }
1569
1581
  if (decision.remember) {
1570
1582
  const freshSession = await deps.sessionsRepo.findById(sessionId);
@@ -1626,21 +1638,18 @@ function createPreToolUseHook(sessionId, taskId, deps) {
1626
1638
  try {
1627
1639
  const errorMessage = error instanceof Error ? error.message : String(error);
1628
1640
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
1629
- await deps.tasksService.patch(taskId, {
1630
- status: TaskStatus.FAILED,
1631
- report: `Error: ${errorMessage}
1641
+ const isPermissionDenial = error instanceof Error && error.message.startsWith("Permission denied for tool:");
1642
+ if (!isPermissionDenial) {
1643
+ await deps.tasksService.patch(taskId, {
1644
+ status: TaskStatus.FAILED,
1645
+ report: `Error: ${errorMessage}
1632
1646
  Timestamp: ${timestamp}`
1633
- });
1647
+ });
1648
+ }
1634
1649
  } catch (updateError) {
1635
1650
  console.error("Failed to update task status:", updateError);
1636
1651
  }
1637
- return {
1638
- hookSpecificOutput: {
1639
- hookEventName: "PreToolUse",
1640
- permissionDecision: "deny",
1641
- permissionDecisionReason: `Permission hook failed: ${error instanceof Error ? error.message : String(error)}`
1642
- }
1643
- };
1652
+ throw error;
1644
1653
  } finally {
1645
1654
  if (releaseLock) {
1646
1655
  releaseLock();
@@ -20,6 +20,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/utils/errors.ts
21
21
  var errors_exports = {};
22
22
  __export(errors_exports, {
23
+ AgorError: () => AgorError,
24
+ AlreadyExistsError: () => AlreadyExistsError,
25
+ NotFoundError: () => NotFoundError,
26
+ UnauthorizedError: () => UnauthorizedError,
27
+ ValidationError: () => ValidationError,
23
28
  createErrorResponse: () => createErrorResponse,
24
29
  formatUserError: () => formatUserError,
25
30
  getErrorDetails: () => getErrorDetails,
@@ -29,6 +34,41 @@ __export(errors_exports, {
29
34
  logError: () => logError
30
35
  });
31
36
  module.exports = __toCommonJS(errors_exports);
37
+ var AgorError = class extends Error {
38
+ constructor(message, code) {
39
+ super(message);
40
+ this.code = code;
41
+ this.name = this.constructor.name;
42
+ if (Error.captureStackTrace) {
43
+ Error.captureStackTrace(this, this.constructor);
44
+ }
45
+ }
46
+ };
47
+ var NotFoundError = class extends AgorError {
48
+ constructor(resourceType, id) {
49
+ super(`${resourceType} not found: ${id}`, "NOT_FOUND");
50
+ this.resourceType = resourceType;
51
+ this.id = id;
52
+ }
53
+ resourceType;
54
+ id;
55
+ };
56
+ var AlreadyExistsError = class extends AgorError {
57
+ constructor(resourceType, identifier) {
58
+ super(`${resourceType} already exists: ${identifier}`, "ALREADY_EXISTS");
59
+ }
60
+ };
61
+ var ValidationError = class extends AgorError {
62
+ constructor(message, field) {
63
+ super(message, "VALIDATION_ERROR");
64
+ this.field = field;
65
+ }
66
+ };
67
+ var UnauthorizedError = class extends AgorError {
68
+ constructor(message = "Not authorized") {
69
+ super(message, "UNAUTHORIZED");
70
+ }
71
+ };
32
72
  function getErrorMessage(error) {
33
73
  if (error instanceof Error) {
34
74
  return error.message;
@@ -90,6 +130,11 @@ function logError(error, context, metadata = {}) {
90
130
  }
91
131
  // Annotate the CommonJS export names for ESM import in node:
92
132
  0 && (module.exports = {
133
+ AgorError,
134
+ AlreadyExistsError,
135
+ NotFoundError,
136
+ UnauthorizedError,
137
+ ValidationError,
93
138
  createErrorResponse,
94
139
  formatUserError,
95
140
  getErrorDetails,
@@ -4,6 +4,43 @@
4
4
  * Provides standardized error formatting, logging, and categorization.
5
5
  * Used by daemon services and API handlers for uniform error responses.
6
6
  */
7
+ /**
8
+ * Custom error classes for type-safe error handling
9
+ */
10
+ /**
11
+ * Base class for all Agor errors
12
+ */
13
+ declare class AgorError extends Error {
14
+ readonly code?: string | undefined;
15
+ constructor(message: string, code?: string | undefined);
16
+ }
17
+ /**
18
+ * Thrown when a resource is not found in the database
19
+ */
20
+ declare class NotFoundError extends AgorError {
21
+ constructor(resourceType: string, id: string);
22
+ readonly resourceType: string;
23
+ readonly id: string;
24
+ }
25
+ /**
26
+ * Thrown when a resource already exists
27
+ */
28
+ declare class AlreadyExistsError extends AgorError {
29
+ constructor(resourceType: string, identifier: string);
30
+ }
31
+ /**
32
+ * Thrown when validation fails
33
+ */
34
+ declare class ValidationError extends AgorError {
35
+ readonly field?: string | undefined;
36
+ constructor(message: string, field?: string | undefined);
37
+ }
38
+ /**
39
+ * Thrown when an operation is not authorized
40
+ */
41
+ declare class UnauthorizedError extends AgorError {
42
+ constructor(message?: string);
43
+ }
7
44
  /**
8
45
  * Extract error message from any value
9
46
  * Handles Error objects, strings, and unknown types
@@ -48,4 +85,4 @@ declare function createErrorResponse(error: unknown, defaultMessage?: string): {
48
85
  */
49
86
  declare function logError(error: unknown, context: string, metadata?: Record<string, unknown>): void;
50
87
 
51
- export { createErrorResponse, formatUserError, getErrorDetails, getErrorMessage, handlePromiseError, isBusinessLogicError, logError };
88
+ export { AgorError, AlreadyExistsError, NotFoundError, UnauthorizedError, ValidationError, createErrorResponse, formatUserError, getErrorDetails, getErrorMessage, handlePromiseError, isBusinessLogicError, logError };
@@ -4,6 +4,43 @@
4
4
  * Provides standardized error formatting, logging, and categorization.
5
5
  * Used by daemon services and API handlers for uniform error responses.
6
6
  */
7
+ /**
8
+ * Custom error classes for type-safe error handling
9
+ */
10
+ /**
11
+ * Base class for all Agor errors
12
+ */
13
+ declare class AgorError extends Error {
14
+ readonly code?: string | undefined;
15
+ constructor(message: string, code?: string | undefined);
16
+ }
17
+ /**
18
+ * Thrown when a resource is not found in the database
19
+ */
20
+ declare class NotFoundError extends AgorError {
21
+ constructor(resourceType: string, id: string);
22
+ readonly resourceType: string;
23
+ readonly id: string;
24
+ }
25
+ /**
26
+ * Thrown when a resource already exists
27
+ */
28
+ declare class AlreadyExistsError extends AgorError {
29
+ constructor(resourceType: string, identifier: string);
30
+ }
31
+ /**
32
+ * Thrown when validation fails
33
+ */
34
+ declare class ValidationError extends AgorError {
35
+ readonly field?: string | undefined;
36
+ constructor(message: string, field?: string | undefined);
37
+ }
38
+ /**
39
+ * Thrown when an operation is not authorized
40
+ */
41
+ declare class UnauthorizedError extends AgorError {
42
+ constructor(message?: string);
43
+ }
7
44
  /**
8
45
  * Extract error message from any value
9
46
  * Handles Error objects, strings, and unknown types
@@ -48,4 +85,4 @@ declare function createErrorResponse(error: unknown, defaultMessage?: string): {
48
85
  */
49
86
  declare function logError(error: unknown, context: string, metadata?: Record<string, unknown>): void;
50
87
 
51
- export { createErrorResponse, formatUserError, getErrorDetails, getErrorMessage, handlePromiseError, isBusinessLogicError, logError };
88
+ export { AgorError, AlreadyExistsError, NotFoundError, UnauthorizedError, ValidationError, createErrorResponse, formatUserError, getErrorDetails, getErrorMessage, handlePromiseError, isBusinessLogicError, logError };
@@ -1,4 +1,39 @@
1
1
  // src/utils/errors.ts
2
+ var AgorError = class extends Error {
3
+ constructor(message, code) {
4
+ super(message);
5
+ this.code = code;
6
+ this.name = this.constructor.name;
7
+ if (Error.captureStackTrace) {
8
+ Error.captureStackTrace(this, this.constructor);
9
+ }
10
+ }
11
+ };
12
+ var NotFoundError = class extends AgorError {
13
+ constructor(resourceType, id) {
14
+ super(`${resourceType} not found: ${id}`, "NOT_FOUND");
15
+ this.resourceType = resourceType;
16
+ this.id = id;
17
+ }
18
+ resourceType;
19
+ id;
20
+ };
21
+ var AlreadyExistsError = class extends AgorError {
22
+ constructor(resourceType, identifier) {
23
+ super(`${resourceType} already exists: ${identifier}`, "ALREADY_EXISTS");
24
+ }
25
+ };
26
+ var ValidationError = class extends AgorError {
27
+ constructor(message, field) {
28
+ super(message, "VALIDATION_ERROR");
29
+ this.field = field;
30
+ }
31
+ };
32
+ var UnauthorizedError = class extends AgorError {
33
+ constructor(message = "Not authorized") {
34
+ super(message, "UNAUTHORIZED");
35
+ }
36
+ };
2
37
  function getErrorMessage(error) {
3
38
  if (error instanceof Error) {
4
39
  return error.message;
@@ -59,6 +94,11 @@ function logError(error, context, metadata = {}) {
59
94
  }
60
95
  }
61
96
  export {
97
+ AgorError,
98
+ AlreadyExistsError,
99
+ NotFoundError,
100
+ UnauthorizedError,
101
+ ValidationError,
62
102
  createErrorResponse,
63
103
  formatUserError,
64
104
  getErrorDetails,
@@ -40,6 +40,10 @@ interface DrizzleAdapterOptions {
40
40
  * Allow multi-record operations (patch/remove without ID)
41
41
  */
42
42
  multi?: boolean | string[];
43
+ /**
44
+ * Resource type name for error messages (e.g., 'Worktree', 'Session')
45
+ */
46
+ resourceType?: string;
43
47
  }
44
48
  /**
45
49
  * Repository interface that the adapter expects
@@ -63,6 +67,7 @@ declare class DrizzleService<T = any, D = Partial<T>, P extends Params = Params>
63
67
  id: string;
64
68
  paginate?: PaginationOptions;
65
69
  multi: boolean | string[];
70
+ resourceType: string;
66
71
  emit?: (event: string, ...args: any[]) => boolean;
67
72
  constructor(repository: Repository<T>, options?: DrizzleAdapterOptions);
68
73
  /**
@@ -1,14 +1,17 @@
1
1
  // src/adapters/drizzle.ts
2
+ import { NotFoundError } from "@agor/core/utils/errors";
2
3
  var DrizzleService = class {
3
4
  constructor(repository, options = {}) {
4
5
  this.repository = repository;
5
6
  this.id = options.id ?? "id";
6
7
  this.paginate = options.paginate;
7
8
  this.multi = options.multi ?? false;
9
+ this.resourceType = options.resourceType ?? "Record";
8
10
  }
9
11
  id;
10
12
  paginate;
11
13
  multi;
14
+ resourceType;
12
15
  // Event emitter for FeathersJS (will be injected by framework)
13
16
  // biome-ignore lint/suspicious/noExplicitAny: FeathersJS event system
14
17
  emit;
@@ -121,7 +124,7 @@ var DrizzleService = class {
121
124
  async get(id, _params) {
122
125
  const result = await this.repository.findById(String(id));
123
126
  if (!result) {
124
- throw new Error(`No record found for id '${id}'`);
127
+ throw new NotFoundError(this.resourceType, String(id));
125
128
  }
126
129
  return result;
127
130
  }
@@ -146,10 +149,7 @@ var DrizzleService = class {
146
149
  * Update a record (complete replacement)
147
150
  */
148
151
  async update(id, data, params) {
149
- const existing = await this.get(id, params);
150
- if (!existing) {
151
- throw new Error(`No record found for id '${id}'`);
152
- }
152
+ await this.get(id, params);
153
153
  const result = await this.repository.update(String(id), data);
154
154
  this.emit?.("updated", result, params);
155
155
  this.emit?.("patched", result, params);
@@ -179,10 +179,7 @@ var DrizzleService = class {
179
179
  }
180
180
  return results;
181
181
  }
182
- const existing = await this.get(id, params);
183
- if (!existing) {
184
- throw new Error(`No record found for id '${id}'`);
185
- }
182
+ await this.get(id, params);
186
183
  const result = await this.repository.update(String(id), data);
187
184
  this.emit?.("patched", result, params);
188
185
  return result;
@@ -205,9 +202,6 @@ var DrizzleService = class {
205
202
  return records;
206
203
  }
207
204
  const existing = await this.get(id, params);
208
- if (!existing) {
209
- throw new Error(`No record found for id '${id}'`);
210
- }
211
205
  await this.repository.delete(String(id));
212
206
  this.emit?.("removed", existing, params);
213
207
  return existing;