@ampcode/plugin 0.0.0-20261006001912-g060eaed → 0.0.0-20261008001818-g5d841b4

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 (3) hide show
  1. package/README.md +6 -0
  2. package/index.d.ts +193 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -13,6 +13,12 @@ Plugins can:
13
13
  - **Show UI** — `ctx.ui.notify(...)`, `ctx.ui.confirm(...)`, `ctx.ui.input(...)`, and `ctx.ui.select(...)`
14
14
  - **Use AI** — `amp.ai.ask(...)` for thread-scoped yes/no classification that defaults to no
15
15
  reasoning unless `{ reasoningEffort }` is set
16
+ - **Decide fast** — `amp.ai.decide(...)`, `amp.ai.choice(...)`, `amp.ai.noul(...)`, and
17
+ `amp.ai.score(...)` for typed decisions with calibrated probabilities and no generated text,
18
+ answered by TypeSafe's Jev, or by OpenAI's Decisions API when you pass
19
+ `{ model: 'openai/gpt-6-luna' }`
20
+ - **Label threads** — `ctx.thread.labels()`, `ctx.thread.addLabels(...)`, and
21
+ `ctx.thread.setLabels(...)` to read and change the current thread's labels
16
22
  - **Upload attachments** — `amp.attachments.upload(...)` to upload image bytes and get back a URL
17
23
  for URL-backed `{ type: 'image', mimeType, url }` tool result blocks, keeping large images out
18
24
  of thread state
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- // sitemap-lastmod: /docs/plugin-api 2026-09-01
1
+ // sitemap-lastmod: /docs/plugin-api 2026-10-06
2
2
 
3
3
  declare module '@ampcode/plugin' {
4
4
  /**
@@ -762,6 +762,134 @@ declare module '@ampcode/plugin' {
762
762
  schema: PluginAIObjectSchema
763
763
  }
764
764
 
765
+ /**
766
+ * Text or JSON accepted wherever a decision takes a state, instructions, or criteria. Objects
767
+ * and arrays are passed to the model as-is.
768
+ */
769
+ export type DecisionInput = string | Record<string, unknown> | unknown[]
770
+
771
+ /** Estimates whether a condition holds for the state. Answers with a probability from 0 to 1. */
772
+ export interface NoulQuestion {
773
+ type: 'noul'
774
+ instructions: DecisionInput
775
+ /** Optional descriptions of what makes the condition true or false. */
776
+ criteria?: { true?: DecisionInput; false?: DecisionInput }
777
+ }
778
+
779
+ /** Rates the state on an ordered scale. `criteria` lists 2–10 levels, lowest first. */
780
+ export interface ScoreQuestion {
781
+ type: 'score'
782
+ instructions: DecisionInput
783
+ criteria: DecisionInput[]
784
+ }
785
+
786
+ /** Chooses one of 2–20 options. `criteria` maps each option to a description or `null`. */
787
+ export interface ChoiceQuestion<Option extends string = string> {
788
+ type: 'choice'
789
+ instructions: DecisionInput
790
+ criteria: Record<Option, DecisionInput | null>
791
+ }
792
+
793
+ export type DecisionQuestion = NoulQuestion | ScoreQuestion | ChoiceQuestion
794
+
795
+ export interface NoulAnswer {
796
+ type: 'noul'
797
+ /** Probability from 0 to 1 that the condition holds. */
798
+ noul: number
799
+ }
800
+
801
+ export interface ScoreAnswer {
802
+ type: 'score'
803
+ /** Probability-weighted score across the levels, from 0 to the number of levels minus one. */
804
+ score: number
805
+ /** Level index (as a string) to the level description. */
806
+ legend: Record<string, string>
807
+ /** Probability of each level index. */
808
+ probabilities: Record<string, number>
809
+ /**
810
+ * The model's confidence in the score, from 0 to 1. Each model defines it; it is not always
811
+ * the probability of the most likely level.
812
+ */
813
+ confidence: number
814
+ }
815
+
816
+ export interface ChoiceAnswer<Option extends string = string> {
817
+ type: 'choice'
818
+ choice: Option
819
+ /** Probability of each option. */
820
+ probabilities: Record<Option, number>
821
+ /**
822
+ * The model's confidence in `choice`, from 0 to 1. Each model defines it; it is not always
823
+ * the probability of `choice`.
824
+ */
825
+ confidence: number
826
+ }
827
+
828
+ export type DecisionAnswer = NoulAnswer | ScoreAnswer | ChoiceAnswer
829
+
830
+ /** The answer type returned for a question of type `Q`. */
831
+ export type DecisionAnswerFor<Q extends DecisionQuestion> =
832
+ Q extends ChoiceQuestion<infer Option>
833
+ ? ChoiceAnswer<Option>
834
+ : Q extends ScoreQuestion
835
+ ? ScoreAnswer
836
+ : NoulAnswer
837
+
838
+ /**
839
+ * A decision model, written as `provider/model`. `typesafe/jev` is TypeSafe's Jev.
840
+ * `openai/gpt-6-luna` is OpenAI's Decisions API. A workspace can disable a provider, so a model
841
+ * that exists here can still be unavailable in a thread.
842
+ *
843
+ * The models answer the same questions but are calibrated differently, so a threshold tuned on
844
+ * one does not carry over to the other. With `openai/gpt-6-luna`, JSON in `state`,
845
+ * `instructions`, and `criteria` is sent as its JSON text, the criteria of a `noul` question are
846
+ * appended to its instructions, and the call rejects if OpenAI declines to answer a question.
847
+ */
848
+ export type DecisionModel = 'typesafe/jev' | 'openai/gpt-6-luna'
849
+
850
+ /** The state every decision is judged against, and the thread that routes the request. */
851
+ export interface DecisionOptions {
852
+ /** Thread to route the request through. Required outside a thread-bound handler. */
853
+ threadID?: ThreadID
854
+ /**
855
+ * The decision model to ask. Defaults to `typesafe/jev`. The call fails if the thread's
856
+ * workspace has disabled that model; Amp never asks another model in its place, so name
857
+ * `openai/gpt-6-luna` for a workspace that allows only OpenAI.
858
+ */
859
+ model?: DecisionModel
860
+ /** The situation to judge: text, or JSON the model reads as-is. Up to 50,000 characters of text. */
861
+ state: DecisionInput
862
+ }
863
+
864
+ export interface DecisionRequest<
865
+ Q extends Record<string, DecisionQuestion> = Record<string, DecisionQuestion>,
866
+ > extends DecisionOptions {
867
+ /** 1–20 questions, each answered independently against `state`. */
868
+ questions: Q
869
+ }
870
+
871
+ export interface DecisionResult<
872
+ Q extends Record<string, DecisionQuestion> = Record<string, DecisionQuestion>,
873
+ > {
874
+ /**
875
+ * The exact model version that answered, as its provider reports it, such as `jev-1.13.0` or
876
+ * `gpt-6-luna`.
877
+ * Record it when you tune thresholds so a model change is visible.
878
+ */
879
+ model: string
880
+ answers: { [K in keyof Q]: DecisionAnswerFor<Q[K]> }
881
+ }
882
+
883
+ /** A single {@link ChoiceQuestion} with its state. */
884
+ export interface ChoiceRequest<Option extends string = string>
885
+ extends DecisionOptions, Omit<ChoiceQuestion<Option>, 'type'> {}
886
+
887
+ /** A single {@link NoulQuestion} with its state. */
888
+ export interface NoulRequest extends DecisionOptions, Omit<NoulQuestion, 'type'> {}
889
+
890
+ /** A single {@link ScoreQuestion} with its state. */
891
+ export interface ScoreRequest extends DecisionOptions, Omit<ScoreQuestion, 'type'> {}
892
+
765
893
  /**
766
894
  * AI capabilities provided to plugins.
767
895
  */
@@ -793,6 +921,49 @@ declare module '@ampcode/plugin' {
793
921
  * @returns Object with result, probability, and reason
794
922
  */
795
923
  ask(question: string, options?: PluginAIAskOptions): Promise<PluginAIAskResult>
924
+
925
+ /**
926
+ * Ask a decision model typed questions about a state. A decision model does not generate
927
+ * text: each answer is a choice, a score, or a yes/no probability (`noul`) with calibrated
928
+ * probabilities, in well under a second and at a small fraction of the cost of
929
+ * {@link PluginAI.generate}. Use it for routing, triage, relevance, and policy checks where
930
+ * the application owns the thresholds and control flow; use {@link PluginAI.generate} when
931
+ * the answer needs reasoning or prose.
932
+ *
933
+ * All questions in one call are answered against the same `state` in one round trip. The
934
+ * answer type of each question is inferred from the request, so `result.answers.route.choice`
935
+ * is typed as the option names you passed. The typed shape is guaranteed; the answer can
936
+ * still be wrong, and adversarial text inside `state` can move it.
937
+ *
938
+ * TypeSafe's Jev answers unless you pass {@link DecisionOptions.model}, and
939
+ * {@link DecisionResult.model} reports the version that answered. The call rejects when the
940
+ * thread's workspace has disabled the model, so handle the error where a decision is
941
+ * optional.
942
+ *
943
+ * Calls are routed through the current thread. Pass `threadID` when calling outside a
944
+ * thread-bound handler.
945
+ */
946
+ decide<Q extends Record<string, DecisionQuestion>>(
947
+ request: DecisionRequest<Q>,
948
+ ): Promise<DecisionResult<Q>>
949
+
950
+ /**
951
+ * Choose one of 2–20 options for a state. Shorthand for {@link PluginAI.decide} with one
952
+ * `choice` question; `answer.choice` is typed as the keys of `criteria`.
953
+ */
954
+ choice<Option extends string>(request: ChoiceRequest<Option>): Promise<ChoiceAnswer<Option>>
955
+
956
+ /**
957
+ * Estimate the probability that a condition holds for a state. Shorthand for
958
+ * {@link PluginAI.decide} with one `noul` question.
959
+ */
960
+ noul(request: NoulRequest): Promise<NoulAnswer>
961
+
962
+ /**
963
+ * Rate a state on an ordered scale of 2–10 levels. Shorthand for {@link PluginAI.decide} with
964
+ * one `score` question.
965
+ */
966
+ score(request: ScoreRequest): Promise<ScoreAnswer>
796
967
  }
797
968
 
798
969
  /**
@@ -1238,6 +1409,27 @@ declare module '@ampcode/plugin' {
1238
1409
  */
1239
1410
  setMultiplayer(options: { ttlSeconds: number | null }): Promise<void>
1240
1411
 
1412
+ /**
1413
+ * Read the thread's labels, sorted alphabetically.
1414
+ */
1415
+ labels(): Promise<string[]>
1416
+
1417
+ /**
1418
+ * Replace the thread's labels and resolve with the resulting labels.
1419
+ *
1420
+ * Labels are lowercased and deduplicated. Each label must be alphanumeric
1421
+ * with hyphens, start with an alphanumeric character, and be at most 32
1422
+ * characters long. A thread holds at most 20 labels. The authenticated user
1423
+ * must be allowed to manage the thread.
1424
+ */
1425
+ setLabels(labels: string[]): Promise<string[]>
1426
+
1427
+ /**
1428
+ * Add labels to the thread, keeping its existing labels, and resolve with
1429
+ * the resulting labels. Same label rules as {@link setLabels}.
1430
+ */
1431
+ addLabels(labels: string[]): Promise<string[]>
1432
+
1241
1433
  /**
1242
1434
  * Read messages from the thread in a stable plugin-facing schema.
1243
1435
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ampcode/plugin",
3
- "version": "0.0.0-20261006001912-g060eaed",
3
+ "version": "0.0.0-20261008001818-g5d841b4",
4
4
  "description": "Amp Plugin API",
5
5
  "homepage": "https://ampcode.com/docs/plugin-api",
6
6
  "author": {