@factory/droid-sdk 0.7.0 → 0.9.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 (53) hide show
  1. package/dist/{chunk-C3KHERVH.js → chunk-FJP5OKS5.js} +6250 -4329
  2. package/dist/chunk-FJP5OKS5.js.map +1 -0
  3. package/dist/{chunk-5UXINOXG.mjs → chunk-KXDG5ZWS.mjs} +8897 -6976
  4. package/dist/chunk-KXDG5ZWS.mjs.map +1 -0
  5. package/dist/{index-D_SzTnFR.d.mts → index-BowvC0_E.d.mts} +43128 -10981
  6. package/dist/{index-D_SzTnFR.d.ts → index-BowvC0_E.d.ts} +43128 -10981
  7. package/dist/index.d.mts +1 -1
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.js +10 -2
  10. package/dist/index.js.map +1 -1
  11. package/dist/index.mjs +9 -1
  12. package/dist/node.d.mts +25 -3
  13. package/dist/node.d.ts +25 -3
  14. package/dist/node.js +565 -423
  15. package/dist/node.js.map +1 -1
  16. package/dist/node.mjs +186 -44
  17. package/dist/node.mjs.map +1 -1
  18. package/docs/typescript-sdk-reference.md +903 -726
  19. package/examples/browser/daemon-model-discovery.ts +47 -0
  20. package/examples/browser/index.html +2 -1
  21. package/examples/browser/list-sessions.ts +1 -3
  22. package/examples/node/abort-session-stream.ts +2 -0
  23. package/examples/node/ask-user-elicitation.ts +11 -7
  24. package/examples/node/compact-session.ts +33 -18
  25. package/examples/node/custom-system-prompt.ts +35 -0
  26. package/examples/node/document-input.ts +58 -0
  27. package/examples/node/enter-spec-mode.ts +50 -29
  28. package/examples/node/external-mcp.ts +45 -0
  29. package/examples/node/fork-session.ts +21 -10
  30. package/examples/node/hook-execution.ts +37 -37
  31. package/examples/node/image-attachment.ts +8 -2
  32. package/examples/node/interrupt-session.ts +27 -8
  33. package/examples/node/model-discovery.ts +28 -0
  34. package/examples/node/model-selection.ts +35 -0
  35. package/examples/node/multi-turn-session.ts +4 -0
  36. package/examples/node/observability.ts +4 -17
  37. package/examples/node/partial-stream.ts +56 -0
  38. package/examples/node/permission-handler.ts +9 -4
  39. package/examples/node/rename-session.ts +7 -3
  40. package/examples/node/result-metadata.ts +10 -1
  41. package/examples/node/resume-session.ts +41 -0
  42. package/examples/node/rewind-session.ts +19 -9
  43. package/examples/node/run.ts +14 -7
  44. package/examples/node/sdk-mcp-tool.ts +10 -8
  45. package/examples/node/session-settings.ts +9 -9
  46. package/examples/node/session-stream.ts +17 -2
  47. package/examples/node/skills.ts +24 -0
  48. package/examples/node/spec-mode-new-session.ts +38 -10
  49. package/examples/node/spec-mode-same-session.ts +37 -3
  50. package/examples/node/structured-output.ts +30 -7
  51. package/package.json +1 -1
  52. package/dist/chunk-5UXINOXG.mjs.map +0 -1
  53. package/dist/chunk-C3KHERVH.js.map +0 -1
@@ -21,10 +21,13 @@ import { tmpdir } from 'node:os';
21
21
  import { join } from 'node:path';
22
22
 
23
23
  import {
24
+ type ClientPermissionHandler,
24
25
  DroidInteractionMode,
26
+ DroidMessageType,
25
27
  ReasoningEffort,
26
28
  SessionNotificationSchema,
27
29
  ToolConfirmationOutcome,
30
+ ToolConfirmationType,
28
31
  createSession,
29
32
  } from '@factory/droid-sdk/node';
30
33
 
@@ -33,11 +36,38 @@ const outputPath = join(tempDir, 'hello.txt');
33
36
 
34
37
  let handoffSessionId: string | null = null;
35
38
 
39
+ const handlePermissionRequest: ClientPermissionHandler = ({
40
+ toolUses,
41
+ options,
42
+ }) => {
43
+ const exitsSpecMode =
44
+ toolUses.length > 0 &&
45
+ toolUses.every(
46
+ (toolUse) => toolUse.details.type === ToolConfirmationType.ExitSpecMode
47
+ );
48
+ const createsExpectedFile =
49
+ toolUses.length > 0 &&
50
+ toolUses.every(
51
+ (toolUse) =>
52
+ toolUse.details.type === ToolConfirmationType.Create &&
53
+ toolUse.details.filePath === outputPath
54
+ );
55
+
56
+ const outcome =
57
+ exitsSpecMode || createsExpectedFile
58
+ ? ToolConfirmationOutcome.ProceedOnce
59
+ : ToolConfirmationOutcome.Cancel;
60
+ if (!options.some((option) => option.value === outcome)) {
61
+ throw new Error(`Outcome not offered: ${outcome}`);
62
+ }
63
+ return outcome;
64
+ };
65
+
36
66
  try {
37
67
  const session = await createSession({
38
68
  interactionMode: DroidInteractionMode.Spec,
39
69
  specModeReasoningEffort: ReasoningEffort.High,
40
- permissionHandler: () => ToolConfirmationOutcome.ProceedOnce,
70
+ permissionHandler: handlePermissionRequest,
41
71
  });
42
72
 
43
73
  // The typed message stream carries no per-message session id, and the
@@ -54,10 +84,14 @@ try {
54
84
  });
55
85
 
56
86
  try {
57
- for await (const _msg of session.stream(
87
+ for await (const message of session.stream(
58
88
  `Plan then create ${outputPath} containing "Hello from Droid".`
59
89
  )) {
60
- // Consume the stream until implementation finishes.
90
+ if (message.type === DroidMessageType.Result && !message.success) {
91
+ throw new Error(
92
+ message.error?.message ?? `Turn ended: ${message.subtype}`
93
+ );
94
+ }
61
95
  }
62
96
  } finally {
63
97
  unsubscribe();
@@ -19,6 +19,31 @@ interface CodeReview {
19
19
  }>;
20
20
  }
21
21
 
22
+ function isCodeReview(value: unknown): value is CodeReview {
23
+ if (
24
+ value === null ||
25
+ typeof value !== 'object' ||
26
+ !('summary' in value) ||
27
+ typeof value.summary !== 'string' ||
28
+ !('findings' in value) ||
29
+ !Array.isArray(value.findings)
30
+ ) {
31
+ return false;
32
+ }
33
+
34
+ return value.findings.every(
35
+ (finding) =>
36
+ finding !== null &&
37
+ typeof finding === 'object' &&
38
+ 'line' in finding &&
39
+ typeof finding.line === 'number' &&
40
+ 'severity' in finding &&
41
+ ['low', 'medium', 'high'].includes(String(finding.severity)) &&
42
+ 'message' in finding &&
43
+ typeof finding.message === 'string'
44
+ );
45
+ }
46
+
22
47
  const result = await run(
23
48
  `Review this code for correctness issues:
24
49
 
@@ -49,6 +74,7 @@ function getDisplayName(user) {
49
74
  },
50
75
  },
51
76
  required: ['summary', 'findings'],
77
+ additionalProperties: false,
52
78
  },
53
79
  },
54
80
  }
@@ -62,12 +88,9 @@ if (!result.success) {
62
88
  throw new Error(`run() failed (${result.subtype}): ${detail}`);
63
89
  }
64
90
 
65
- // A successful run can still omit structured output, for example when the
66
- // model answers in prose instead of the requested schema.
67
- if (!result.structuredOutput) {
68
- throw new Error('run() succeeded but returned no structured output.');
91
+ // The SDK returns `unknown`; validate it before reading fields.
92
+ if (!isCodeReview(result.structuredOutput)) {
93
+ throw new Error('run() returned an unexpected structured output shape.');
69
94
  }
70
95
 
71
- const review = result.structuredOutput as CodeReview;
72
-
73
- console.log(JSON.stringify(review, null, 2));
96
+ console.log(JSON.stringify(result.structuredOutput, null, 2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@factory/droid-sdk",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "private": false,
5
5
  "description": "TypeScript SDK for the Factory Droid CLI",
6
6
  "keywords": [