@ghostpaw/questlog 0.9.1 → 0.9.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.
package/dist/index.js CHANGED
@@ -3181,8 +3181,153 @@ function translateToolError(error, options = {}) {
3181
3181
  );
3182
3182
  }
3183
3183
 
3184
+ // src/surfaces/schema_actions.ts
3185
+ var requirements = /* @__PURE__ */ new WeakMap();
3186
+ function registerSchemaActions(schema, actions) {
3187
+ requirements.set(schema, actions);
3188
+ }
3189
+ function requiredForAction(schema, action) {
3190
+ return requirements.get(schema)?.[String(action)] ?? [];
3191
+ }
3192
+
3193
+ // src/surfaces/schema_examples.ts
3194
+ function minimalInput(schema, action, name = "") {
3195
+ const constant = "const" in schema ? schema.const : void 0;
3196
+ if (constant !== void 0) return constant;
3197
+ if (schema.enum)
3198
+ return action !== void 0 && schema.enum.includes(action) ? action : schema.enum[0];
3199
+ const branch = schema.oneOf?.[0];
3200
+ if (schema.type === "object" || schema.properties) {
3201
+ action ??= schema.properties?.action?.enum?.[0];
3202
+ const properties = { ...schema.properties, ...branch?.properties };
3203
+ return Object.fromEntries(
3204
+ [
3205
+ .../* @__PURE__ */ new Set([
3206
+ ...schema.required ?? [],
3207
+ ...requiredForAction(schema, action),
3208
+ ...schema.properties?.outcome && ["finish", "abandon", "abandon_and_spawn_followups"].includes(String(action)) ? ["outcome"] : [],
3209
+ ...branch?.required ?? []
3210
+ ])
3211
+ ].map((key) => [
3212
+ key,
3213
+ minimalInput(properties[key] ?? {}, key === "action" ? action : void 0, key)
3214
+ ])
3215
+ );
3216
+ }
3217
+ if (branch) return minimalInput(branch, action, name);
3218
+ if (schema.type === "array") return [minimalInput(schema.items ?? {}, void 0, name)];
3219
+ if (schema.type === "integer" || schema.type === "number")
3220
+ return name === "anchorAt" ? 18e11 : name === "toQuestId" ? 2 : 1;
3221
+ if (schema.type === "boolean") return false;
3222
+ if (schema.type === "null") return null;
3223
+ if (name === "rrule") return "FREQ=WEEKLY";
3224
+ if (name === "title") return "Review release notes";
3225
+ if (name === "objective") return "Check changes against the release";
3226
+ return "release";
3227
+ }
3228
+
3229
+ // src/surfaces/validate_input.ts
3230
+ function validateInput(schema, input, path = "$") {
3231
+ const errors = [];
3232
+ const matchesType = (type) => {
3233
+ switch (type) {
3234
+ case "object":
3235
+ return typeof input === "object" && input !== null && !Array.isArray(input);
3236
+ case "array":
3237
+ return Array.isArray(input);
3238
+ case "integer":
3239
+ return typeof input === "number" && Number.isInteger(input);
3240
+ case "number":
3241
+ return typeof input === "number" && Number.isFinite(input);
3242
+ case "null":
3243
+ return input === null;
3244
+ default:
3245
+ return typeof input === type;
3246
+ }
3247
+ };
3248
+ if (schema.type && !(Array.isArray(schema.type) ? schema.type : [schema.type]).some(matchesType)) {
3249
+ return [`${path}: expected ${String(schema.type)}`];
3250
+ }
3251
+ if (schema.enum && !schema.enum.some((value) => JSON.stringify(value) === JSON.stringify(input)))
3252
+ errors.push(`${path}: expected one of ${JSON.stringify(schema.enum)}`);
3253
+ const constant = "const" in schema ? schema.const : void 0;
3254
+ if (constant !== void 0 && constant !== input)
3255
+ errors.push(`${path}: expected ${JSON.stringify(constant)}`);
3256
+ if (schema.oneOf) {
3257
+ const branches = schema.oneOf.map((branch) => validateInput(branch, input, path));
3258
+ if (!branches.some((errors2) => errors2.length === 0)) {
3259
+ errors.push(
3260
+ ...branches.sort((a, b) => a.length - b.length)[0] ?? [
3261
+ `${path}: must match at least one schema branch`
3262
+ ]
3263
+ );
3264
+ }
3265
+ }
3266
+ if (typeof input === "object" && input !== null && !Array.isArray(input)) {
3267
+ const record = input;
3268
+ for (const name of [...schema.required ?? [], ...requiredForAction(schema, record.action)])
3269
+ if (!Object.hasOwn(record, name)) errors.push(`${path}.${name}: required`);
3270
+ for (const [name, property] of Object.entries(schema.properties ?? {}))
3271
+ if (Object.hasOwn(record, name))
3272
+ errors.push(...validateInput(property, record[name], `${path}.${name}`));
3273
+ }
3274
+ if (Array.isArray(input) && schema.items)
3275
+ input.forEach((value, index) => {
3276
+ errors.push(...validateInput(schema.items, value, `${path}[${index}]`));
3277
+ });
3278
+ return errors;
3279
+ }
3280
+
3281
+ // src/tools/action_requirements.ts
3282
+ var actionRequirements = {
3283
+ shape_work: {
3284
+ create_quest: ["quest"],
3285
+ create_questline: ["questline"],
3286
+ attach_quest_to_questline: ["questId", "questlineId"],
3287
+ detach_quest_from_questline: ["questId"],
3288
+ dismiss_rumor: ["rumorId"],
3289
+ reopen_rumor: ["rumorId"],
3290
+ settle_rumor: ["rumorId"],
3291
+ update_rumor: ["rumorId"]
3292
+ },
3293
+ manage_repeatable: {
3294
+ create: ["repeatableQuest"],
3295
+ archive: ["repeatableQuestId"],
3296
+ update: ["repeatableQuestId"],
3297
+ spawn_due: ["now"]
3298
+ },
3299
+ organize_work: {
3300
+ add_unlock: ["fromQuestId", "toQuestId"],
3301
+ remove_unlock: ["fromQuestId", "toQuestId"],
3302
+ replace_unlocks: ["fromQuestIds", "toQuestId"],
3303
+ archive_questline: ["questlineId"],
3304
+ set_questline_fields: ["questlineId"]
3305
+ },
3306
+ plan_quest: { revise_objective: ["objective"] },
3307
+ run_quest: {
3308
+ abandon_and_spawn_followups: ["followups"],
3309
+ log_effort: ["effortSeconds"]
3310
+ },
3311
+ reward_work: { add: ["reward"], update: ["reward"], replace_repeatable_template: ["rewards"] }
3312
+ };
3313
+
3184
3314
  // src/tools/tool_metadata.ts
3185
3315
  function defineQuestlogTool(definition) {
3316
+ const requirements2 = actionRequirements[definition.name];
3317
+ if (requirements2) registerSchemaActions(definition.inputSchema, requirements2);
3318
+ const handler = definition.handler;
3319
+ definition.handler = (db, input) => {
3320
+ const errors = validateInput(definition.inputSchema, input);
3321
+ if (errors.length) {
3322
+ const action = input?.action;
3323
+ const example = minimalInput(definition.inputSchema, action);
3324
+ const message = errors.join("; ");
3325
+ return toolFailure("protocol", "invalid_input", message, message, {
3326
+ recovery: `${message}; pass ${JSON.stringify(example)}`
3327
+ });
3328
+ }
3329
+ return handler(db, input);
3330
+ };
3186
3331
  return definition;
3187
3332
  }
3188
3333
  function stringSchema(description, options = {}) {
@@ -4725,7 +4870,7 @@ var rewardWorkTool = defineQuestlogTool({
4725
4870
  name: stringSchema("Reward name."),
4726
4871
  now: integerSchema("Optional update timestamp for the update action.")
4727
4872
  },
4728
- []
4873
+ ["kind", "name"]
4729
4874
  ),
4730
4875
  rewards: arraySchema(
4731
4876
  objectSchema(
@@ -5391,7 +5536,7 @@ var shapeWorkTool = defineQuestlogTool({
5391
5536
  title: stringSchema("Questline title."),
5392
5537
  description: stringSchema("Optional questline description.")
5393
5538
  },
5394
- []
5539
+ ["title"]
5395
5540
  ),
5396
5541
  rumorId: integerSchema("Rumor to settle, dismiss, or reopen."),
5397
5542
  dismissedAt: integerSchema("Optional dismissal timestamp."),
@@ -5,14 +5,74 @@ import { homedir } from 'node:os';
5
5
 
6
6
  var __defProp = Object.defineProperty;
7
7
  var __getOwnPropNames = Object.getOwnPropertyNames;
8
- var __esm = (fn, res) => function __init() {
9
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ var __esm = (fn, res, err) => function __init() {
9
+ if (err) throw err[0];
10
+ try {
11
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
+ } catch (e) {
13
+ throw err = [e], e;
14
+ }
10
15
  };
11
16
  var __export = (target, all) => {
12
17
  for (var name in all)
13
18
  __defProp(target, name, { get: all[name], enumerable: true });
14
19
  };
15
20
 
21
+ // src/surfaces/schema_actions.ts
22
+ function registerSchemaActions(schema, actions) {
23
+ requirements.set(schema, actions);
24
+ }
25
+ function requiredForAction(schema, action) {
26
+ return requirements.get(schema)?.[String(action)] ?? [];
27
+ }
28
+ var requirements;
29
+ var init_schema_actions = __esm({
30
+ "src/surfaces/schema_actions.ts"() {
31
+ requirements = /* @__PURE__ */ new WeakMap();
32
+ }
33
+ });
34
+
35
+ // src/surfaces/schema_examples.ts
36
+ function minimalInput(schema, action, name = "") {
37
+ const constant = "const" in schema ? schema.const : void 0;
38
+ if (constant !== void 0) return constant;
39
+ if (schema.enum)
40
+ return action !== void 0 && schema.enum.includes(action) ? action : schema.enum[0];
41
+ const branch = schema.oneOf?.[0];
42
+ if (schema.type === "object" || schema.properties) {
43
+ action ??= schema.properties?.action?.enum?.[0];
44
+ const properties = { ...schema.properties, ...branch?.properties };
45
+ return Object.fromEntries(
46
+ [
47
+ .../* @__PURE__ */ new Set([
48
+ ...schema.required ?? [],
49
+ ...requiredForAction(schema, action),
50
+ ...schema.properties?.outcome && ["finish", "abandon", "abandon_and_spawn_followups"].includes(String(action)) ? ["outcome"] : [],
51
+ ...branch?.required ?? []
52
+ ])
53
+ ].map((key) => [
54
+ key,
55
+ minimalInput(properties[key] ?? {}, key === "action" ? action : void 0, key)
56
+ ])
57
+ );
58
+ }
59
+ if (branch) return minimalInput(branch, action, name);
60
+ if (schema.type === "array") return [minimalInput(schema.items ?? {}, void 0, name)];
61
+ if (schema.type === "integer" || schema.type === "number")
62
+ return name === "anchorAt" ? 18e11 : name === "toQuestId" ? 2 : 1;
63
+ if (schema.type === "boolean") return false;
64
+ if (schema.type === "null") return null;
65
+ if (name === "rrule") return "FREQ=WEEKLY";
66
+ if (name === "title") return "Review release notes";
67
+ if (name === "objective") return "Check changes against the release";
68
+ return "release";
69
+ }
70
+ var init_schema_examples = __esm({
71
+ "src/surfaces/schema_examples.ts"() {
72
+ init_schema_actions();
73
+ }
74
+ });
75
+
16
76
  // src/surfaces/surface_types.ts
17
77
  function isErrorResult(result) {
18
78
  return typeof result === "object" && result !== null && "ok" in result && result.ok === false;
@@ -46,11 +106,22 @@ function validateInput(schema, input, path = "$") {
46
106
  }
47
107
  if (schema.enum && !schema.enum.some((value) => JSON.stringify(value) === JSON.stringify(input)))
48
108
  errors.push(`${path}: expected one of ${JSON.stringify(schema.enum)}`);
49
- if (schema.oneOf && !schema.oneOf.some((branch) => validateInput(branch, input, path).length === 0))
50
- errors.push(`${path}: must match at least one schema branch`);
109
+ const constant = "const" in schema ? schema.const : void 0;
110
+ if (constant !== void 0 && constant !== input)
111
+ errors.push(`${path}: expected ${JSON.stringify(constant)}`);
112
+ if (schema.oneOf) {
113
+ const branches = schema.oneOf.map((branch) => validateInput(branch, input, path));
114
+ if (!branches.some((errors2) => errors2.length === 0)) {
115
+ errors.push(
116
+ ...branches.sort((a, b) => a.length - b.length)[0] ?? [
117
+ `${path}: must match at least one schema branch`
118
+ ]
119
+ );
120
+ }
121
+ }
51
122
  if (typeof input === "object" && input !== null && !Array.isArray(input)) {
52
123
  const record = input;
53
- for (const name of schema.required ?? [])
124
+ for (const name of [...schema.required ?? [], ...requiredForAction(schema, record.action)])
54
125
  if (!Object.hasOwn(record, name)) errors.push(`${path}.${name}: required`);
55
126
  for (const [name, property] of Object.entries(schema.properties ?? {}))
56
127
  if (Object.hasOwn(record, name))
@@ -64,6 +135,7 @@ function validateInput(schema, input, path = "$") {
64
135
  }
65
136
  var init_validate_input = __esm({
66
137
  "src/surfaces/validate_input.ts"() {
138
+ init_schema_actions();
67
139
  }
68
140
  });
69
141
 
@@ -158,7 +230,15 @@ Not for: ${tool.whenNotToUse}` : ""),
158
230
  const input = params.arguments === void 0 ? {} : params.arguments;
159
231
  const errors = validateInput(tool.inputSchema, input);
160
232
  if (errors.length)
161
- return result({ content: [{ type: "text", text: errors.join("\n") }], isError: true });
233
+ return result({
234
+ content: [
235
+ {
236
+ type: "text",
237
+ text: `${errors.join("\n")}; pass ${JSON.stringify(minimalInput(tool.inputSchema, input?.action))}`
238
+ }
239
+ ],
240
+ isError: true
241
+ });
162
242
  try {
163
243
  context ??= Promise.resolve().then(options.context);
164
244
  const value = await tool.handler(await context, input);
@@ -197,6 +277,7 @@ Not for: ${tool.whenNotToUse}` : ""),
197
277
  var MCP_LATEST_PROTOCOL_VERSION, MCP_SUPPORTED_PROTOCOL_VERSIONS;
198
278
  var init_mcp_server = __esm({
199
279
  "src/surfaces/mcp_server.ts"() {
280
+ init_schema_examples();
200
281
  init_surface_types();
201
282
  init_validate_input();
202
283
  MCP_LATEST_PROTOCOL_VERSION = "2026-07-28";
@@ -677,7 +758,7 @@ var init_open_questlog_db = __esm({
677
758
  var questlogVersion;
678
759
  var init_version = __esm({
679
760
  "src/version.ts"() {
680
- questlogVersion = "0.9.1";
761
+ questlogVersion = "0.9.2";
681
762
  }
682
763
  });
683
764
 
@@ -979,8 +1060,61 @@ var init_tool_errors = __esm({
979
1060
  }
980
1061
  });
981
1062
 
1063
+ // src/tools/action_requirements.ts
1064
+ var actionRequirements;
1065
+ var init_action_requirements = __esm({
1066
+ "src/tools/action_requirements.ts"() {
1067
+ actionRequirements = {
1068
+ shape_work: {
1069
+ create_quest: ["quest"],
1070
+ create_questline: ["questline"],
1071
+ attach_quest_to_questline: ["questId", "questlineId"],
1072
+ detach_quest_from_questline: ["questId"],
1073
+ dismiss_rumor: ["rumorId"],
1074
+ reopen_rumor: ["rumorId"],
1075
+ settle_rumor: ["rumorId"],
1076
+ update_rumor: ["rumorId"]
1077
+ },
1078
+ manage_repeatable: {
1079
+ create: ["repeatableQuest"],
1080
+ archive: ["repeatableQuestId"],
1081
+ update: ["repeatableQuestId"],
1082
+ spawn_due: ["now"]
1083
+ },
1084
+ organize_work: {
1085
+ add_unlock: ["fromQuestId", "toQuestId"],
1086
+ remove_unlock: ["fromQuestId", "toQuestId"],
1087
+ replace_unlocks: ["fromQuestIds", "toQuestId"],
1088
+ archive_questline: ["questlineId"],
1089
+ set_questline_fields: ["questlineId"]
1090
+ },
1091
+ plan_quest: { revise_objective: ["objective"] },
1092
+ run_quest: {
1093
+ abandon_and_spawn_followups: ["followups"],
1094
+ log_effort: ["effortSeconds"]
1095
+ },
1096
+ reward_work: { add: ["reward"], update: ["reward"], replace_repeatable_template: ["rewards"] }
1097
+ };
1098
+ }
1099
+ });
1100
+
982
1101
  // src/tools/tool_metadata.ts
983
1102
  function defineQuestlogTool(definition) {
1103
+ const requirements2 = actionRequirements[definition.name];
1104
+ if (requirements2) registerSchemaActions(definition.inputSchema, requirements2);
1105
+ const handler = definition.handler;
1106
+ definition.handler = (db, input) => {
1107
+ const errors = validateInput(definition.inputSchema, input);
1108
+ if (errors.length) {
1109
+ const action = input?.action;
1110
+ const example = minimalInput(definition.inputSchema, action);
1111
+ const message = errors.join("; ");
1112
+ return toolFailure("protocol", "invalid_input", message, message, {
1113
+ recovery: `${message}; pass ${JSON.stringify(example)}`
1114
+ });
1115
+ }
1116
+ return handler(db, input);
1117
+ };
984
1118
  return definition;
985
1119
  }
986
1120
  function stringSchema(description, options = {}) {
@@ -1023,6 +1157,11 @@ function objectSchema(properties, required, description) {
1023
1157
  }
1024
1158
  var init_tool_metadata = __esm({
1025
1159
  "src/tools/tool_metadata.ts"() {
1160
+ init_schema_actions();
1161
+ init_schema_examples();
1162
+ init_validate_input();
1163
+ init_action_requirements();
1164
+ init_tool_types();
1026
1165
  }
1027
1166
  });
1028
1167
 
@@ -4599,7 +4738,7 @@ var init_reward_work_tool = __esm({
4599
4738
  name: stringSchema("Reward name."),
4600
4739
  now: integerSchema("Optional update timestamp for the update action.")
4601
4740
  },
4602
- []
4741
+ ["kind", "name"]
4603
4742
  ),
4604
4743
  rewards: arraySchema(
4605
4744
  objectSchema(
@@ -5513,7 +5652,7 @@ var init_shape_work_tool = __esm({
5513
5652
  title: stringSchema("Questline title."),
5514
5653
  description: stringSchema("Optional questline description.")
5515
5654
  },
5516
- []
5655
+ ["title"]
5517
5656
  ),
5518
5657
  rumorId: integerSchema("Rumor to settle, dismiss, or reopen."),
5519
5658
  dismissedAt: integerSchema("Optional dismissal timestamp."),
@@ -5867,7 +6006,7 @@ var init_questlog_surface_tools = __esm({
5867
6006
  whenNotToUse: tool.whenNotToUse ?? "",
5868
6007
  readOnly: tool.readOnly,
5869
6008
  sideEffects: tool.sideEffects,
5870
- inputSchema: { ...tool.inputSchema, type: "object" },
6009
+ inputSchema: tool.inputSchema,
5871
6010
  handler: tool.handler
5872
6011
  })
5873
6012
  );
package/dist/mcp.cjs CHANGED
@@ -386,7 +386,7 @@ async function openQuestlogDb(path) {
386
386
  }
387
387
 
388
388
  // src/version.ts
389
- var questlogVersion = "0.9.1";
389
+ var questlogVersion = "0.9.2";
390
390
 
391
391
  // src/surface/questlog_mcp_instructions.ts
392
392
  var questlogMcpInstructions = "Questlog manages local tasks, calendar commitments, and recurring work. Search before creating work, inspect before changing it, and keep uncertain work as rumors. Use unlock prerequisites to sequence work. run_quest refuses blocked starts unless force is true. Review commitments and resolve work with an honest outcome.";
@@ -632,8 +632,153 @@ function translateToolError(error, options = {}) {
632
632
  );
633
633
  }
634
634
 
635
+ // src/surfaces/schema_actions.ts
636
+ var requirements = /* @__PURE__ */ new WeakMap();
637
+ function registerSchemaActions(schema, actions) {
638
+ requirements.set(schema, actions);
639
+ }
640
+ function requiredForAction(schema, action) {
641
+ return requirements.get(schema)?.[String(action)] ?? [];
642
+ }
643
+
644
+ // src/surfaces/schema_examples.ts
645
+ function minimalInput(schema, action, name = "") {
646
+ const constant = "const" in schema ? schema.const : void 0;
647
+ if (constant !== void 0) return constant;
648
+ if (schema.enum)
649
+ return action !== void 0 && schema.enum.includes(action) ? action : schema.enum[0];
650
+ const branch = schema.oneOf?.[0];
651
+ if (schema.type === "object" || schema.properties) {
652
+ action ??= schema.properties?.action?.enum?.[0];
653
+ const properties = { ...schema.properties, ...branch?.properties };
654
+ return Object.fromEntries(
655
+ [
656
+ .../* @__PURE__ */ new Set([
657
+ ...schema.required ?? [],
658
+ ...requiredForAction(schema, action),
659
+ ...schema.properties?.outcome && ["finish", "abandon", "abandon_and_spawn_followups"].includes(String(action)) ? ["outcome"] : [],
660
+ ...branch?.required ?? []
661
+ ])
662
+ ].map((key) => [
663
+ key,
664
+ minimalInput(properties[key] ?? {}, key === "action" ? action : void 0, key)
665
+ ])
666
+ );
667
+ }
668
+ if (branch) return minimalInput(branch, action, name);
669
+ if (schema.type === "array") return [minimalInput(schema.items ?? {}, void 0, name)];
670
+ if (schema.type === "integer" || schema.type === "number")
671
+ return name === "anchorAt" ? 18e11 : name === "toQuestId" ? 2 : 1;
672
+ if (schema.type === "boolean") return false;
673
+ if (schema.type === "null") return null;
674
+ if (name === "rrule") return "FREQ=WEEKLY";
675
+ if (name === "title") return "Review release notes";
676
+ if (name === "objective") return "Check changes against the release";
677
+ return "release";
678
+ }
679
+
680
+ // src/surfaces/validate_input.ts
681
+ function validateInput(schema, input, path = "$") {
682
+ const errors = [];
683
+ const matchesType = (type) => {
684
+ switch (type) {
685
+ case "object":
686
+ return typeof input === "object" && input !== null && !Array.isArray(input);
687
+ case "array":
688
+ return Array.isArray(input);
689
+ case "integer":
690
+ return typeof input === "number" && Number.isInteger(input);
691
+ case "number":
692
+ return typeof input === "number" && Number.isFinite(input);
693
+ case "null":
694
+ return input === null;
695
+ default:
696
+ return typeof input === type;
697
+ }
698
+ };
699
+ if (schema.type && !(Array.isArray(schema.type) ? schema.type : [schema.type]).some(matchesType)) {
700
+ return [`${path}: expected ${String(schema.type)}`];
701
+ }
702
+ if (schema.enum && !schema.enum.some((value) => JSON.stringify(value) === JSON.stringify(input)))
703
+ errors.push(`${path}: expected one of ${JSON.stringify(schema.enum)}`);
704
+ const constant = "const" in schema ? schema.const : void 0;
705
+ if (constant !== void 0 && constant !== input)
706
+ errors.push(`${path}: expected ${JSON.stringify(constant)}`);
707
+ if (schema.oneOf) {
708
+ const branches = schema.oneOf.map((branch) => validateInput(branch, input, path));
709
+ if (!branches.some((errors2) => errors2.length === 0)) {
710
+ errors.push(
711
+ ...branches.sort((a, b) => a.length - b.length)[0] ?? [
712
+ `${path}: must match at least one schema branch`
713
+ ]
714
+ );
715
+ }
716
+ }
717
+ if (typeof input === "object" && input !== null && !Array.isArray(input)) {
718
+ const record = input;
719
+ for (const name of [...schema.required ?? [], ...requiredForAction(schema, record.action)])
720
+ if (!Object.hasOwn(record, name)) errors.push(`${path}.${name}: required`);
721
+ for (const [name, property] of Object.entries(schema.properties ?? {}))
722
+ if (Object.hasOwn(record, name))
723
+ errors.push(...validateInput(property, record[name], `${path}.${name}`));
724
+ }
725
+ if (Array.isArray(input) && schema.items)
726
+ input.forEach((value, index) => {
727
+ errors.push(...validateInput(schema.items, value, `${path}[${index}]`));
728
+ });
729
+ return errors;
730
+ }
731
+
732
+ // src/tools/action_requirements.ts
733
+ var actionRequirements = {
734
+ shape_work: {
735
+ create_quest: ["quest"],
736
+ create_questline: ["questline"],
737
+ attach_quest_to_questline: ["questId", "questlineId"],
738
+ detach_quest_from_questline: ["questId"],
739
+ dismiss_rumor: ["rumorId"],
740
+ reopen_rumor: ["rumorId"],
741
+ settle_rumor: ["rumorId"],
742
+ update_rumor: ["rumorId"]
743
+ },
744
+ manage_repeatable: {
745
+ create: ["repeatableQuest"],
746
+ archive: ["repeatableQuestId"],
747
+ update: ["repeatableQuestId"],
748
+ spawn_due: ["now"]
749
+ },
750
+ organize_work: {
751
+ add_unlock: ["fromQuestId", "toQuestId"],
752
+ remove_unlock: ["fromQuestId", "toQuestId"],
753
+ replace_unlocks: ["fromQuestIds", "toQuestId"],
754
+ archive_questline: ["questlineId"],
755
+ set_questline_fields: ["questlineId"]
756
+ },
757
+ plan_quest: { revise_objective: ["objective"] },
758
+ run_quest: {
759
+ abandon_and_spawn_followups: ["followups"],
760
+ log_effort: ["effortSeconds"]
761
+ },
762
+ reward_work: { add: ["reward"], update: ["reward"], replace_repeatable_template: ["rewards"] }
763
+ };
764
+
635
765
  // src/tools/tool_metadata.ts
636
766
  function defineQuestlogTool(definition) {
767
+ const requirements2 = actionRequirements[definition.name];
768
+ if (requirements2) registerSchemaActions(definition.inputSchema, requirements2);
769
+ const handler = definition.handler;
770
+ definition.handler = (db, input) => {
771
+ const errors = validateInput(definition.inputSchema, input);
772
+ if (errors.length) {
773
+ const action = input?.action;
774
+ const example = minimalInput(definition.inputSchema, action);
775
+ const message = errors.join("; ");
776
+ return toolFailure("protocol", "invalid_input", message, message, {
777
+ recovery: `${message}; pass ${JSON.stringify(example)}`
778
+ });
779
+ }
780
+ return handler(db, input);
781
+ };
637
782
  return definition;
638
783
  }
639
784
  function stringSchema(description, options = {}) {
@@ -3583,7 +3728,7 @@ var rewardWorkTool = defineQuestlogTool({
3583
3728
  name: stringSchema("Reward name."),
3584
3729
  now: integerSchema("Optional update timestamp for the update action.")
3585
3730
  },
3586
- []
3731
+ ["kind", "name"]
3587
3732
  ),
3588
3733
  rewards: arraySchema(
3589
3734
  objectSchema(
@@ -4410,7 +4555,7 @@ var shapeWorkTool = defineQuestlogTool({
4410
4555
  title: stringSchema("Questline title."),
4411
4556
  description: stringSchema("Optional questline description.")
4412
4557
  },
4413
- []
4558
+ ["title"]
4414
4559
  ),
4415
4560
  rumorId: integerSchema("Rumor to settle, dismiss, or reopen."),
4416
4561
  dismissedAt: integerSchema("Optional dismissal timestamp."),
@@ -4692,7 +4837,7 @@ var questlogSurfaceTools = questlogTools.map(
4692
4837
  whenNotToUse: tool.whenNotToUse ?? "",
4693
4838
  readOnly: tool.readOnly,
4694
4839
  sideEffects: tool.sideEffects,
4695
- inputSchema: { ...tool.inputSchema, type: "object" },
4840
+ inputSchema: tool.inputSchema,
4696
4841
  handler: tool.handler
4697
4842
  })
4698
4843
  );
@@ -4727,47 +4872,6 @@ function isErrorResult(result) {
4727
4872
  return typeof result === "object" && result !== null && "ok" in result && result.ok === false;
4728
4873
  }
4729
4874
 
4730
- // src/surfaces/validate_input.ts
4731
- function validateInput(schema, input, path = "$") {
4732
- const errors = [];
4733
- const matchesType = (type) => {
4734
- switch (type) {
4735
- case "object":
4736
- return typeof input === "object" && input !== null && !Array.isArray(input);
4737
- case "array":
4738
- return Array.isArray(input);
4739
- case "integer":
4740
- return typeof input === "number" && Number.isInteger(input);
4741
- case "number":
4742
- return typeof input === "number" && Number.isFinite(input);
4743
- case "null":
4744
- return input === null;
4745
- default:
4746
- return typeof input === type;
4747
- }
4748
- };
4749
- if (schema.type && !(Array.isArray(schema.type) ? schema.type : [schema.type]).some(matchesType)) {
4750
- return [`${path}: expected ${String(schema.type)}`];
4751
- }
4752
- if (schema.enum && !schema.enum.some((value) => JSON.stringify(value) === JSON.stringify(input)))
4753
- errors.push(`${path}: expected one of ${JSON.stringify(schema.enum)}`);
4754
- if (schema.oneOf && !schema.oneOf.some((branch) => validateInput(branch, input, path).length === 0))
4755
- errors.push(`${path}: must match at least one schema branch`);
4756
- if (typeof input === "object" && input !== null && !Array.isArray(input)) {
4757
- const record = input;
4758
- for (const name of schema.required ?? [])
4759
- if (!Object.hasOwn(record, name)) errors.push(`${path}.${name}: required`);
4760
- for (const [name, property] of Object.entries(schema.properties ?? {}))
4761
- if (Object.hasOwn(record, name))
4762
- errors.push(...validateInput(property, record[name], `${path}.${name}`));
4763
- }
4764
- if (Array.isArray(input) && schema.items)
4765
- input.forEach((value, index) => {
4766
- errors.push(...validateInput(schema.items, value, `${path}[${index}]`));
4767
- });
4768
- return errors;
4769
- }
4770
-
4771
4875
  // src/surfaces/mcp_server.ts
4772
4876
  var MCP_LATEST_PROTOCOL_VERSION = "2026-07-28";
4773
4877
  var MCP_SUPPORTED_PROTOCOL_VERSIONS = [
@@ -4867,7 +4971,15 @@ Not for: ${tool.whenNotToUse}` : ""),
4867
4971
  const input = params.arguments === void 0 ? {} : params.arguments;
4868
4972
  const errors = validateInput(tool.inputSchema, input);
4869
4973
  if (errors.length)
4870
- return result({ content: [{ type: "text", text: errors.join("\n") }], isError: true });
4974
+ return result({
4975
+ content: [
4976
+ {
4977
+ type: "text",
4978
+ text: `${errors.join("\n")}; pass ${JSON.stringify(minimalInput(tool.inputSchema, input?.action))}`
4979
+ }
4980
+ ],
4981
+ isError: true
4982
+ });
4871
4983
  try {
4872
4984
  context ??= Promise.resolve().then(options.context);
4873
4985
  const value = await tool.handler(await context, input);