@hyperdrive.bot/paseo-protocol 0.3.67 → 0.3.69

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.
@@ -51,6 +51,28 @@ export declare const KanbanBoardConfigSchema: z.ZodObject<{
51
51
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
52
52
  }, z.core.$loose>;
53
53
  export type KanbanBoardConfig = z.infer<typeof KanbanBoardConfigSchema>;
54
+ /**
55
+ * MARRA CUSTOMIZATION - workspace-wide tag colors.
56
+ *
57
+ * A LIST, not a record, because `DaemonConfigStore.patch` deep-merges: it recurses
58
+ * into plain objects and skips `undefined`, so a key could never be deleted from a
59
+ * record and "reset this tag to Auto" would silently do nothing. Arrays are
60
+ * replaced wholesale, which makes un-pinning an ordinary write. Same constraint,
61
+ * and same solution, as `kanbanBoard.buckets`.
62
+ */
63
+ export declare const TagColorOverrideSchema: z.ZodObject<{
64
+ tag: z.ZodString;
65
+ color: z.ZodEnum<{
66
+ red: "red";
67
+ orange: "orange";
68
+ amber: "amber";
69
+ green: "green";
70
+ teal: "teal";
71
+ blue: "blue";
72
+ purple: "purple";
73
+ pink: "pink";
74
+ }>;
75
+ }, z.core.$strip>;
54
76
  export declare const MutableDaemonConfigSchema: z.ZodObject<{
55
77
  mcp: z.ZodObject<{
56
78
  injectIntoAgents: z.ZodBoolean;
@@ -92,6 +114,19 @@ export declare const MutableDaemonConfigSchema: z.ZodObject<{
92
114
  }, z.core.$loose>>>;
93
115
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
94
116
  }, z.core.$loose>>;
117
+ tagColors: z.ZodOptional<z.ZodArray<z.ZodObject<{
118
+ tag: z.ZodString;
119
+ color: z.ZodEnum<{
120
+ red: "red";
121
+ orange: "orange";
122
+ amber: "amber";
123
+ green: "green";
124
+ teal: "teal";
125
+ blue: "blue";
126
+ purple: "purple";
127
+ pink: "pink";
128
+ }>;
129
+ }, z.core.$strip>>>;
95
130
  }, z.core.$loose>;
96
131
  export declare const MutableDaemonConfigPatchSchema: z.ZodObject<{
97
132
  mcp: z.ZodOptional<z.ZodOptional<z.ZodObject<{
@@ -134,6 +169,19 @@ export declare const MutableDaemonConfigPatchSchema: z.ZodObject<{
134
169
  }, z.core.$loose>>>;
135
170
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
136
171
  }, z.core.$loose>>>;
172
+ tagColors: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodObject<{
173
+ tag: z.ZodString;
174
+ color: z.ZodEnum<{
175
+ red: "red";
176
+ orange: "orange";
177
+ amber: "amber";
178
+ green: "green";
179
+ teal: "teal";
180
+ blue: "blue";
181
+ purple: "purple";
182
+ pink: "pink";
183
+ }>;
184
+ }, z.core.$strip>>>>;
137
185
  }, z.core.$loose>;
138
186
  export type MutableDaemonConfig = z.infer<typeof MutableDaemonConfigSchema>;
139
187
  export type MutableDaemonConfigPatch = z.infer<typeof MutableDaemonConfigPatchSchema>;
@@ -2131,6 +2179,19 @@ export declare const SetDaemonConfigRequestMessageSchema: z.ZodObject<{
2131
2179
  }, z.core.$loose>>>;
2132
2180
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
2133
2181
  }, z.core.$loose>>>;
2182
+ tagColors: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodObject<{
2183
+ tag: z.ZodString;
2184
+ color: z.ZodEnum<{
2185
+ red: "red";
2186
+ orange: "orange";
2187
+ amber: "amber";
2188
+ green: "green";
2189
+ teal: "teal";
2190
+ blue: "blue";
2191
+ purple: "purple";
2192
+ pink: "pink";
2193
+ }>;
2194
+ }, z.core.$strip>>>>;
2134
2195
  }, z.core.$loose>;
2135
2196
  }, z.core.$strip>;
2136
2197
  export declare const ReadProjectConfigRequestMessageSchema: z.ZodObject<{
@@ -4506,6 +4567,19 @@ export declare const SessionInboundMessageSchema: z.ZodDiscriminatedUnion<[z.Zod
4506
4567
  }, z.core.$loose>>>;
4507
4568
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
4508
4569
  }, z.core.$loose>>>;
4570
+ tagColors: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodObject<{
4571
+ tag: z.ZodString;
4572
+ color: z.ZodEnum<{
4573
+ red: "red";
4574
+ orange: "orange";
4575
+ amber: "amber";
4576
+ green: "green";
4577
+ teal: "teal";
4578
+ blue: "blue";
4579
+ purple: "purple";
4580
+ pink: "pink";
4581
+ }>;
4582
+ }, z.core.$strip>>>>;
4509
4583
  }, z.core.$loose>;
4510
4584
  }, z.core.$strip>, z.ZodObject<{
4511
4585
  type: z.ZodLiteral<"read_project_config_request">;
@@ -6517,6 +6591,19 @@ export declare const DaemonConfigChangedStatusPayloadSchema: z.ZodObject<{
6517
6591
  }, z.core.$loose>>>;
6518
6592
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
6519
6593
  }, z.core.$loose>>;
6594
+ tagColors: z.ZodOptional<z.ZodArray<z.ZodObject<{
6595
+ tag: z.ZodString;
6596
+ color: z.ZodEnum<{
6597
+ red: "red";
6598
+ orange: "orange";
6599
+ amber: "amber";
6600
+ green: "green";
6601
+ teal: "teal";
6602
+ blue: "blue";
6603
+ purple: "purple";
6604
+ pink: "pink";
6605
+ }>;
6606
+ }, z.core.$strip>>>;
6520
6607
  }, z.core.$loose>;
6521
6608
  }, z.core.$loose>;
6522
6609
  export declare const KnownStatusPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -6854,6 +6941,19 @@ export declare const KnownStatusPayloadSchema: z.ZodDiscriminatedUnion<[z.ZodObj
6854
6941
  }, z.core.$loose>>>;
6855
6942
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
6856
6943
  }, z.core.$loose>>;
6944
+ tagColors: z.ZodOptional<z.ZodArray<z.ZodObject<{
6945
+ tag: z.ZodString;
6946
+ color: z.ZodEnum<{
6947
+ red: "red";
6948
+ orange: "orange";
6949
+ amber: "amber";
6950
+ green: "green";
6951
+ teal: "teal";
6952
+ blue: "blue";
6953
+ purple: "purple";
6954
+ pink: "pink";
6955
+ }>;
6956
+ }, z.core.$strip>>>;
6857
6957
  }, z.core.$loose>;
6858
6958
  }, z.core.$loose>], "status">;
6859
6959
  export type KnownStatusPayload = z.infer<typeof KnownStatusPayloadSchema>;
@@ -11388,6 +11488,19 @@ export declare const GetDaemonConfigResponseMessageSchema: z.ZodObject<{
11388
11488
  }, z.core.$loose>>>;
11389
11489
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
11390
11490
  }, z.core.$loose>>;
11491
+ tagColors: z.ZodOptional<z.ZodArray<z.ZodObject<{
11492
+ tag: z.ZodString;
11493
+ color: z.ZodEnum<{
11494
+ red: "red";
11495
+ orange: "orange";
11496
+ amber: "amber";
11497
+ green: "green";
11498
+ teal: "teal";
11499
+ blue: "blue";
11500
+ purple: "purple";
11501
+ pink: "pink";
11502
+ }>;
11503
+ }, z.core.$strip>>>;
11391
11504
  }, z.core.$loose>;
11392
11505
  }, z.core.$loose>;
11393
11506
  }, z.core.$strip>;
@@ -11476,6 +11589,19 @@ export declare const SetDaemonConfigResponseMessageSchema: z.ZodObject<{
11476
11589
  }, z.core.$loose>>>;
11477
11590
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
11478
11591
  }, z.core.$loose>>;
11592
+ tagColors: z.ZodOptional<z.ZodArray<z.ZodObject<{
11593
+ tag: z.ZodString;
11594
+ color: z.ZodEnum<{
11595
+ red: "red";
11596
+ orange: "orange";
11597
+ amber: "amber";
11598
+ green: "green";
11599
+ teal: "teal";
11600
+ blue: "blue";
11601
+ purple: "purple";
11602
+ pink: "pink";
11603
+ }>;
11604
+ }, z.core.$strip>>>;
11479
11605
  }, z.core.$loose>;
11480
11606
  }, z.core.$loose>;
11481
11607
  }, z.core.$strip>;
@@ -17773,6 +17899,19 @@ export declare const SessionOutboundMessageSchema: z.ZodDiscriminatedUnion<[z.Zo
17773
17899
  }, z.core.$loose>>>;
17774
17900
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
17775
17901
  }, z.core.$loose>>;
17902
+ tagColors: z.ZodOptional<z.ZodArray<z.ZodObject<{
17903
+ tag: z.ZodString;
17904
+ color: z.ZodEnum<{
17905
+ red: "red";
17906
+ orange: "orange";
17907
+ amber: "amber";
17908
+ green: "green";
17909
+ teal: "teal";
17910
+ blue: "blue";
17911
+ purple: "purple";
17912
+ pink: "pink";
17913
+ }>;
17914
+ }, z.core.$strip>>>;
17776
17915
  }, z.core.$loose>;
17777
17916
  }, z.core.$loose>;
17778
17917
  }, z.core.$strip>, z.ZodObject<{
@@ -17820,6 +17959,19 @@ export declare const SessionOutboundMessageSchema: z.ZodDiscriminatedUnion<[z.Zo
17820
17959
  }, z.core.$loose>>>;
17821
17960
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
17822
17961
  }, z.core.$loose>>;
17962
+ tagColors: z.ZodOptional<z.ZodArray<z.ZodObject<{
17963
+ tag: z.ZodString;
17964
+ color: z.ZodEnum<{
17965
+ red: "red";
17966
+ orange: "orange";
17967
+ amber: "amber";
17968
+ green: "green";
17969
+ teal: "teal";
17970
+ blue: "blue";
17971
+ purple: "purple";
17972
+ pink: "pink";
17973
+ }>;
17974
+ }, z.core.$strip>>>;
17823
17975
  }, z.core.$loose>;
17824
17976
  }, z.core.$loose>;
17825
17977
  }, z.core.$strip>, z.ZodObject<{
@@ -22920,6 +23072,19 @@ export declare const WSSessionInboundSchema: z.ZodObject<{
22920
23072
  }, z.core.$loose>>>;
22921
23073
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
22922
23074
  }, z.core.$loose>>>;
23075
+ tagColors: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodObject<{
23076
+ tag: z.ZodString;
23077
+ color: z.ZodEnum<{
23078
+ red: "red";
23079
+ orange: "orange";
23080
+ amber: "amber";
23081
+ green: "green";
23082
+ teal: "teal";
23083
+ blue: "blue";
23084
+ purple: "purple";
23085
+ pink: "pink";
23086
+ }>;
23087
+ }, z.core.$strip>>>>;
22923
23088
  }, z.core.$loose>;
22924
23089
  }, z.core.$strip>, z.ZodObject<{
22925
23090
  type: z.ZodLiteral<"read_project_config_request">;
@@ -28256,6 +28421,19 @@ export declare const WSSessionOutboundSchema: z.ZodObject<{
28256
28421
  }, z.core.$loose>>>;
28257
28422
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
28258
28423
  }, z.core.$loose>>;
28424
+ tagColors: z.ZodOptional<z.ZodArray<z.ZodObject<{
28425
+ tag: z.ZodString;
28426
+ color: z.ZodEnum<{
28427
+ red: "red";
28428
+ orange: "orange";
28429
+ amber: "amber";
28430
+ green: "green";
28431
+ teal: "teal";
28432
+ blue: "blue";
28433
+ purple: "purple";
28434
+ pink: "pink";
28435
+ }>;
28436
+ }, z.core.$strip>>>;
28259
28437
  }, z.core.$loose>;
28260
28438
  }, z.core.$loose>;
28261
28439
  }, z.core.$strip>, z.ZodObject<{
@@ -28303,6 +28481,19 @@ export declare const WSSessionOutboundSchema: z.ZodObject<{
28303
28481
  }, z.core.$loose>>>;
28304
28482
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
28305
28483
  }, z.core.$loose>>;
28484
+ tagColors: z.ZodOptional<z.ZodArray<z.ZodObject<{
28485
+ tag: z.ZodString;
28486
+ color: z.ZodEnum<{
28487
+ red: "red";
28488
+ orange: "orange";
28489
+ amber: "amber";
28490
+ green: "green";
28491
+ teal: "teal";
28492
+ blue: "blue";
28493
+ purple: "purple";
28494
+ pink: "pink";
28495
+ }>;
28496
+ }, z.core.$strip>>>;
28306
28497
  }, z.core.$loose>;
28307
28498
  }, z.core.$loose>;
28308
28499
  }, z.core.$strip>, z.ZodObject<{
@@ -33059,6 +33250,19 @@ export declare const WSInboundMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObjec
33059
33250
  }, z.core.$loose>>>;
33060
33251
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
33061
33252
  }, z.core.$loose>>>;
33253
+ tagColors: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodObject<{
33254
+ tag: z.ZodString;
33255
+ color: z.ZodEnum<{
33256
+ red: "red";
33257
+ orange: "orange";
33258
+ amber: "amber";
33259
+ green: "green";
33260
+ teal: "teal";
33261
+ blue: "blue";
33262
+ purple: "purple";
33263
+ pink: "pink";
33264
+ }>;
33265
+ }, z.core.$strip>>>>;
33062
33266
  }, z.core.$loose>;
33063
33267
  }, z.core.$strip>, z.ZodObject<{
33064
33268
  type: z.ZodLiteral<"read_project_config_request">;
@@ -38397,6 +38601,19 @@ export declare const WSOutboundMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObje
38397
38601
  }, z.core.$loose>>>;
38398
38602
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
38399
38603
  }, z.core.$loose>>;
38604
+ tagColors: z.ZodOptional<z.ZodArray<z.ZodObject<{
38605
+ tag: z.ZodString;
38606
+ color: z.ZodEnum<{
38607
+ red: "red";
38608
+ orange: "orange";
38609
+ amber: "amber";
38610
+ green: "green";
38611
+ teal: "teal";
38612
+ blue: "blue";
38613
+ purple: "purple";
38614
+ pink: "pink";
38615
+ }>;
38616
+ }, z.core.$strip>>>;
38400
38617
  }, z.core.$loose>;
38401
38618
  }, z.core.$loose>;
38402
38619
  }, z.core.$strip>, z.ZodObject<{
@@ -38444,6 +38661,19 @@ export declare const WSOutboundMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObje
38444
38661
  }, z.core.$loose>>>;
38445
38662
  defaultBucketId: z.ZodDefault<z.ZodNullable<z.ZodString>>;
38446
38663
  }, z.core.$loose>>;
38664
+ tagColors: z.ZodOptional<z.ZodArray<z.ZodObject<{
38665
+ tag: z.ZodString;
38666
+ color: z.ZodEnum<{
38667
+ red: "red";
38668
+ orange: "orange";
38669
+ amber: "amber";
38670
+ green: "green";
38671
+ teal: "teal";
38672
+ blue: "blue";
38673
+ purple: "purple";
38674
+ pink: "pink";
38675
+ }>;
38676
+ }, z.core.$strip>>>;
38447
38677
  }, z.core.$loose>;
38448
38678
  }, z.core.$loose>;
38449
38679
  }, z.core.$strip>, z.ZodObject<{
package/dist/messages.js CHANGED
@@ -5,6 +5,7 @@ import { AGENT_LIFECYCLE_STATUSES } from "./agent-lifecycle.js";
5
5
  import { MAX_EXPLICIT_AGENT_TITLE_CHARS } from "./agent-title-limits.js";
6
6
  import { AgentProviderSchema } from "./provider-manifest.js";
7
7
  import { TOOL_CALL_ICON_NAMES } from "./agent-types.js";
8
+ import { TAG_COLOR_NAMES } from "./tag-color.js";
8
9
  import { FileBeginMetadataSchema } from "./binary-frames/file-transfer.js";
9
10
  import { ChatCreateRequestSchema, ChatListRequestSchema, ChatInspectRequestSchema, ChatDeleteRequestSchema, ChatPostRequestSchema, ChatReadRequestSchema, ChatWaitRequestSchema, ChatCreateResponseSchema, ChatListResponseSchema, ChatInspectResponseSchema, ChatDeleteResponseSchema, ChatPostResponseSchema, ChatReadResponseSchema, ChatWaitResponseSchema, } from "./chat/rpc-schemas.js";
10
11
  import { ScheduleCreateRequestSchema, ScheduleListRequestSchema, ScheduleInspectRequestSchema, ScheduleLogsRequestSchema, SchedulePauseRequestSchema, ScheduleResumeRequestSchema, ScheduleDeleteRequestSchema, ScheduleRunOnceRequestSchema, ScheduleUpdateRequestSchema, ScheduleCreateResponseSchema, ScheduleListResponseSchema, ScheduleInspectResponseSchema, ScheduleLogsResponseSchema, SchedulePauseResponseSchema, ScheduleResumeResponseSchema, ScheduleDeleteResponseSchema, ScheduleRunOnceResponseSchema, ScheduleUpdateResponseSchema, } from "./schedule/rpc-schemas.js";
@@ -92,6 +93,19 @@ const MutableBrowserToolsConfigSchema = z
92
93
  enabled: z.boolean().default(false),
93
94
  })
94
95
  .passthrough();
96
+ /**
97
+ * MARRA CUSTOMIZATION - workspace-wide tag colors.
98
+ *
99
+ * A LIST, not a record, because `DaemonConfigStore.patch` deep-merges: it recurses
100
+ * into plain objects and skips `undefined`, so a key could never be deleted from a
101
+ * record and "reset this tag to Auto" would silently do nothing. Arrays are
102
+ * replaced wholesale, which makes un-pinning an ordinary write. Same constraint,
103
+ * and same solution, as `kanbanBoard.buckets`.
104
+ */
105
+ export const TagColorOverrideSchema = z.object({
106
+ tag: z.string().min(1),
107
+ color: z.enum(TAG_COLOR_NAMES),
108
+ });
95
109
  export const MutableDaemonConfigSchema = z
96
110
  .object({
97
111
  mcp: z
@@ -112,6 +126,11 @@ export const MutableDaemonConfigSchema = z
112
126
  // Optional so an old daemon (which never sends it) and an old client (which
113
127
  // ignores it) both keep parsing this config unchanged.
114
128
  kanbanBoard: KanbanBoardConfigSchema.optional(),
129
+ // COMPAT(tagColors): optional for the same reason kanbanBoard is. An old
130
+ // daemon never sends it and the app falls back to the per-tag hash, which is
131
+ // exactly the behaviour that shipped in v0.3.65 - so there is nothing to
132
+ // migrate and nothing to backfill.
133
+ tagColors: z.array(TagColorOverrideSchema).optional(),
115
134
  })
116
135
  .passthrough();
117
136
  export const MutableDaemonConfigPatchSchema = z
@@ -131,6 +150,10 @@ export const MutableDaemonConfigPatchSchema = z
131
150
  // carrying `kanbanBoard` must carry the complete board. That is exactly what
132
151
  // the client sends (it owns the full board in memory before every write).
133
152
  kanbanBoard: KanbanBoardConfigSchema.optional(),
153
+ // Patched wholesale for the same reason kanbanBoard is: deepMerge replaces
154
+ // arrays but can never delete a record key, so the client always sends the
155
+ // COMPLETE override list. That is what makes "reset to Auto" expressible.
156
+ tagColors: z.array(TagColorOverrideSchema).optional(),
134
157
  })
135
158
  .partial()
136
159
  .passthrough();
@@ -14,9 +14,17 @@
14
14
  *
15
15
  * Resolution order, deliberately in that order:
16
16
  * 1. An explicit, recognized color name in the label value wins.
17
- * 2. Anything else (including the legacy "true", and any unrecognized name)
17
+ * 2. A workspace-wide override for that tag name (the daemon config's
18
+ * `tagColors`, edited from Settings > Appearance) comes next.
19
+ * 3. Anything else (including the legacy "true", and any unrecognized name)
18
20
  * falls through to a stable hash of the tag name.
19
21
  *
22
+ * WHY THE LABEL STILL OUTRANKS THE GLOBAL MAP: the label value is the per-session
23
+ * exception - one agent deliberately painted odd, and the hand-edit escape hatch
24
+ * for `$PASEO_HOME/agents/*.json`. A global default that could silently overrule a
25
+ * value someone typed into a specific session's file would make that file a lie.
26
+ * The global map is a better DEFAULT, not a bigger hammer.
27
+ *
20
28
  * Consequence worth stating: every session stored before this shipped lights up
21
29
  * correctly on first render, because the color is a function of data we already
22
30
  * have. Nothing backfills, nothing rewrites "true".
@@ -56,6 +64,41 @@ export declare function isTagColorName(value: string | null | undefined): value
56
64
  * keeps the accumulator unsigned after Math.imul, so the index is never negative.
57
65
  */
58
66
  export declare function hashTagToColorIndex(tag: string): number;
67
+ /**
68
+ * One workspace-wide pin: "every tag named `monumenta` is teal, unless a specific
69
+ * session says otherwise".
70
+ *
71
+ * Stored as a LIST rather than a record, and that is load-bearing rather than
72
+ * stylistic. The daemon config is patched through `deepMerge`, which recurses into
73
+ * plain objects and skips `undefined` - so a record could never have a key
74
+ * REMOVED by a patch, and "reset this tag back to Auto" would silently no-op
75
+ * forever. `deepMerge` replaces arrays wholesale, so a list makes un-pinning a
76
+ * normal write. `kanbanBoard.buckets` carries the same constraint for the same
77
+ * reason.
78
+ */
79
+ export interface TagColorOverride {
80
+ tag: string;
81
+ color: TagColorName;
82
+ }
83
+ /** The resolved lookup form. Built once, then handed to every resolve call. */
84
+ export type TagColorOverrides = ReadonlyMap<string, TagColorName>;
85
+ /**
86
+ * The canonical key for a tag name. Must stay identical to the normalization in
87
+ * `hashTagToColorIndex`, or a tag would hash under one spelling and be pinned
88
+ * under another.
89
+ */
90
+ export declare function normalizeTagKey(tag: string): string;
91
+ /**
92
+ * Fold an override list into a lookup map, dropping anything malformed.
93
+ *
94
+ * Unknown color names and blank tags are SKIPPED rather than thrown on, matching
95
+ * this module's standing contract that a hand-edited config can never break the
96
+ * UI - the worst a bad entry does is fall through to the hash. Later entries win,
97
+ * so a list that somehow carries a tag twice still resolves deterministically.
98
+ */
99
+ export declare function buildTagColorOverrides(list: readonly TagColorOverride[] | null | undefined): TagColorOverrides;
100
+ /** An empty override map. Shared so callers do not allocate one per render. */
101
+ export declare const NO_TAG_COLOR_OVERRIDES: TagColorOverrides;
59
102
  /**
60
103
  * The color name for a tag, given the raw label value stored alongside it.
61
104
  *
@@ -65,7 +108,7 @@ export declare function hashTagToColorIndex(tag: string): number;
65
108
  * a human editing ~/.paseo/agents/*.json by hand without being able to break the
66
109
  * UI.
67
110
  */
68
- export declare function resolveTagColorName(tag: string, labelValue?: string | null): TagColorName;
111
+ export declare function resolveTagColorName(tag: string, labelValue?: string | null, overrides?: TagColorOverrides | null): TagColorName;
69
112
  /** One tag paired with the color it resolves to. */
70
113
  export interface ColoredTag {
71
114
  tag: string;
@@ -78,7 +121,7 @@ export interface ColoredTag {
78
121
  * an unsorted map iteration would let the same session paint its bands in a
79
122
  * different order on desktop than on mobile.
80
123
  */
81
- export declare function coloredTagsFromLabels(labels: Record<string, string> | null | undefined, tagPrefix?: string): ColoredTag[];
124
+ export declare function coloredTagsFromLabels(labels: Record<string, string> | null | undefined, tagPrefix?: string, overrides?: TagColorOverrides | null): ColoredTag[];
82
125
  /** Re-exported so callers can write the legacy value without hardcoding it. */
83
126
  export declare const TAG_LABEL_PRESENT_VALUE = "true";
84
127
  //# sourceMappingURL=tag-color.d.ts.map
package/dist/tag-color.js CHANGED
@@ -14,9 +14,17 @@
14
14
  *
15
15
  * Resolution order, deliberately in that order:
16
16
  * 1. An explicit, recognized color name in the label value wins.
17
- * 2. Anything else (including the legacy "true", and any unrecognized name)
17
+ * 2. A workspace-wide override for that tag name (the daemon config's
18
+ * `tagColors`, edited from Settings > Appearance) comes next.
19
+ * 3. Anything else (including the legacy "true", and any unrecognized name)
18
20
  * falls through to a stable hash of the tag name.
19
21
  *
22
+ * WHY THE LABEL STILL OUTRANKS THE GLOBAL MAP: the label value is the per-session
23
+ * exception - one agent deliberately painted odd, and the hand-edit escape hatch
24
+ * for `$PASEO_HOME/agents/*.json`. A global default that could silently overrule a
25
+ * value someone typed into a specific session's file would make that file a lie.
26
+ * The global map is a better DEFAULT, not a bigger hammer.
27
+ *
20
28
  * Consequence worth stating: every session stored before this shipped lights up
21
29
  * correctly on first render, because the color is a function of data we already
22
30
  * have. Nothing backfills, nothing rewrites "true".
@@ -79,6 +87,38 @@ export function hashTagToColorIndex(tag) {
79
87
  }
80
88
  return hash % TAG_COLOR_NAMES.length;
81
89
  }
90
+ /**
91
+ * The canonical key for a tag name. Must stay identical to the normalization in
92
+ * `hashTagToColorIndex`, or a tag would hash under one spelling and be pinned
93
+ * under another.
94
+ */
95
+ export function normalizeTagKey(tag) {
96
+ return tag.trim().toLowerCase();
97
+ }
98
+ /**
99
+ * Fold an override list into a lookup map, dropping anything malformed.
100
+ *
101
+ * Unknown color names and blank tags are SKIPPED rather than thrown on, matching
102
+ * this module's standing contract that a hand-edited config can never break the
103
+ * UI - the worst a bad entry does is fall through to the hash. Later entries win,
104
+ * so a list that somehow carries a tag twice still resolves deterministically.
105
+ */
106
+ export function buildTagColorOverrides(list) {
107
+ const map = new Map();
108
+ if (!list)
109
+ return map;
110
+ for (const entry of list) {
111
+ if (!entry || typeof entry.tag !== "string" || !isTagColorName(entry.color))
112
+ continue;
113
+ const key = normalizeTagKey(entry.tag);
114
+ if (key.length === 0)
115
+ continue;
116
+ map.set(key, entry.color.trim().toLowerCase());
117
+ }
118
+ return map;
119
+ }
120
+ /** An empty override map. Shared so callers do not allocate one per render. */
121
+ export const NO_TAG_COLOR_OVERRIDES = new Map();
82
122
  /**
83
123
  * The color name for a tag, given the raw label value stored alongside it.
84
124
  *
@@ -88,10 +128,13 @@ export function hashTagToColorIndex(tag) {
88
128
  * a human editing ~/.paseo/agents/*.json by hand without being able to break the
89
129
  * UI.
90
130
  */
91
- export function resolveTagColorName(tag, labelValue) {
131
+ export function resolveTagColorName(tag, labelValue, overrides) {
92
132
  if (isTagColorName(labelValue)) {
93
133
  return labelValue.trim().toLowerCase();
94
134
  }
135
+ const pinned = overrides?.get(normalizeTagKey(tag));
136
+ if (pinned)
137
+ return pinned;
95
138
  return TAG_COLOR_NAMES[hashTagToColorIndex(tag)];
96
139
  }
97
140
  /**
@@ -101,7 +144,7 @@ export function resolveTagColorName(tag, labelValue) {
101
144
  * an unsorted map iteration would let the same session paint its bands in a
102
145
  * different order on desktop than on mobile.
103
146
  */
104
- export function coloredTagsFromLabels(labels, tagPrefix = TAG_LABEL_PREFIX) {
147
+ export function coloredTagsFromLabels(labels, tagPrefix = TAG_LABEL_PREFIX, overrides) {
105
148
  if (!labels)
106
149
  return [];
107
150
  const result = [];
@@ -112,7 +155,7 @@ export function coloredTagsFromLabels(labels, tagPrefix = TAG_LABEL_PREFIX) {
112
155
  if (value === ABSENT_VALUE)
113
156
  continue;
114
157
  const tag = key.slice(tagPrefix.length);
115
- result.push({ tag, color: resolveTagColorName(tag, value) });
158
+ result.push({ tag, color: resolveTagColorName(tag, value, overrides) });
116
159
  }
117
160
  result.sort((a, b) => a.tag.localeCompare(b.tag));
118
161
  return result;
@@ -3999,6 +3999,19 @@ export declare const WSOutboundMessageSchema: {
3999
3999
  }, import("zod/v4/core").$loose>>>;
4000
4000
  defaultBucketId: import("zod").ZodDefault<import("zod").ZodNullable<import("zod").ZodString>>;
4001
4001
  }, import("zod/v4/core").$loose>>;
4002
+ tagColors: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodObject<{
4003
+ tag: import("zod").ZodString;
4004
+ color: import("zod").ZodEnum<{
4005
+ red: "red";
4006
+ orange: "orange";
4007
+ amber: "amber";
4008
+ green: "green";
4009
+ teal: "teal";
4010
+ blue: "blue";
4011
+ purple: "purple";
4012
+ pink: "pink";
4013
+ }>;
4014
+ }, import("zod/v4/core").$strip>>>;
4002
4015
  }, import("zod/v4/core").$loose>;
4003
4016
  }, import("zod/v4/core").$loose>;
4004
4017
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
@@ -4046,6 +4059,19 @@ export declare const WSOutboundMessageSchema: {
4046
4059
  }, import("zod/v4/core").$loose>>>;
4047
4060
  defaultBucketId: import("zod").ZodDefault<import("zod").ZodNullable<import("zod").ZodString>>;
4048
4061
  }, import("zod/v4/core").$loose>>;
4062
+ tagColors: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodObject<{
4063
+ tag: import("zod").ZodString;
4064
+ color: import("zod").ZodEnum<{
4065
+ red: "red";
4066
+ orange: "orange";
4067
+ amber: "amber";
4068
+ green: "green";
4069
+ teal: "teal";
4070
+ blue: "blue";
4071
+ purple: "purple";
4072
+ pink: "pink";
4073
+ }>;
4074
+ }, import("zod/v4/core").$strip>>>;
4049
4075
  }, import("zod/v4/core").$loose>;
4050
4076
  }, import("zod/v4/core").$loose>;
4051
4077
  }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperdrive.bot/paseo-protocol",
3
- "version": "0.3.67",
3
+ "version": "0.3.69",
4
4
  "description": "Paseo shared protocol schemas and wire types",
5
5
  "files": [
6
6
  "dist",