@xfey/tutti 0.1.14 → 0.1.16

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 (62) hide show
  1. package/dist/collaboration-state/index.d.ts +2 -2
  2. package/dist/collaboration-state/index.js +1 -1
  3. package/dist/collaboration-state/scratchpad.d.ts +2 -2
  4. package/dist/collaboration-state/scratchpad.js +94 -7
  5. package/dist/collaboration-state/storage-types.d.ts +14 -0
  6. package/dist/collaboration-state/types.d.ts +9 -1
  7. package/dist/control-plane/task-compile-output.js +7 -3
  8. package/dist/control-plane/task-compile-start.js +1 -1
  9. package/dist/control-plane/workflows/openai.d.ts +1 -0
  10. package/dist/control-plane/workflows/openai.js +5 -0
  11. package/dist/providers/openai/app-server/read-only-procedure.d.ts +1 -0
  12. package/dist/providers/openai/app-server/read-only-procedure.js +3 -1
  13. package/dist/providers/openai/app-server/skills.d.ts +2 -2
  14. package/dist/providers/openai/app-server/skills.js +8 -7
  15. package/dist/providers/openai/app-server/workspace-write-run.d.ts +1 -0
  16. package/dist/providers/openai/app-server/workspace-write-run.js +4 -2
  17. package/dist/providers/openai/chat-assistant.d.ts +1 -0
  18. package/dist/providers/openai/chat-assistant.js +3 -0
  19. package/dist/run-pipeline/openai.d.ts +1 -0
  20. package/dist/run-pipeline/openai.js +2 -0
  21. package/dist/run-pipeline/task-run-invocation.js +1 -0
  22. package/dist/server-shell/cli/host-server-runtime.js +16 -1
  23. package/dist/server-shell/cli/project-identity.d.ts +3 -0
  24. package/dist/server-shell/cli/project-identity.js +24 -0
  25. package/dist/server-shell/http/routes/agent-context-shared.d.ts +1 -0
  26. package/dist/server-shell/http/routes/agent-context-shared.js +4 -0
  27. package/dist/server-shell/http/routes/agent-context-skills-routes.js +3 -3
  28. package/dist/server-shell/http/routes/project-api/invalidations.d.ts +1 -0
  29. package/dist/server-shell/http/routes/project-api/invalidations.js +4 -1
  30. package/dist/server-shell/http/routes/project-api/openapi-collaboration-routes.d.ts +14 -1
  31. package/dist/server-shell/http/routes/project-api/openapi-workspace-routes.d.ts +22 -1
  32. package/dist/server-shell/http/routes/project-api/openapi-workspace-routes.js +14 -1
  33. package/dist/server-shell/http/routes/project-api/openapi.d.ts +36 -2
  34. package/dist/server-shell/http/routes/project-api/payload-validation.d.ts +2 -1
  35. package/dist/server-shell/http/routes/project-api/payload-validation.js +7 -0
  36. package/dist/server-shell/http/routes/project-api/schemas.d.ts +22 -1
  37. package/dist/server-shell/http/routes/project-api/schemas.js +1 -0
  38. package/dist/server-shell/http/routes/project-api/skills-helpers.d.ts +1 -0
  39. package/dist/server-shell/http/routes/project-api/skills-helpers.js +4 -1
  40. package/dist/server-shell/http/routes/project-api/skills-routes.js +5 -5
  41. package/dist/server-shell/http/routes/project-api/system-routes.js +66 -4
  42. package/dist/server-shell/http/routes/project-api/types.d.ts +2 -0
  43. package/dist/skills/index.d.ts +7 -6
  44. package/dist/skills/index.js +18 -21
  45. package/migrations/0012_scratchpad_receipts.sql +31 -0
  46. package/migrations/README.md +2 -1
  47. package/node_modules/@tutti/shared/dist/schemas/api/index.d.ts +2 -2
  48. package/node_modules/@tutti/shared/dist/schemas/api/index.js +2 -2
  49. package/node_modules/@tutti/shared/dist/schemas/api/runtime-events.d.ts +14 -1
  50. package/node_modules/@tutti/shared/dist/schemas/api/types.d.ts +7 -2
  51. package/node_modules/@tutti/shared/dist/schemas/api/work-items.d.ts +24 -1
  52. package/node_modules/@tutti/shared/dist/schemas/api/work-items.js +15 -1
  53. package/node_modules/@tutti/shared/dist/schemas/api/workspace-commands.d.ts +46 -2
  54. package/node_modules/@tutti/shared/dist/schemas/api/workspace-commands.js +15 -0
  55. package/package.json +1 -1
  56. package/prompts/prompt-flow-map.md +5 -5
  57. package/prompts/skills/read-user-skills/SKILL.md +1 -1
  58. package/web/assets/index-DoZOFmIq.js +29 -0
  59. package/web/assets/index-mXkNKFjm.css +1 -0
  60. package/web/index.html +8 -2
  61. package/web/assets/index-Cm_uyHbH.js +0 -29
  62. package/web/assets/index-DIQpGeKY.css +0 -1
@@ -5,6 +5,7 @@ import { createProjectId, ID_PREFIXES, isPrefixedId } from "@tutti/shared/ids";
5
5
  import { LaunchError } from "./errors.js";
6
6
  export const PROJECT_ID_GIT_CONFIG_KEY = "tutti.project-id";
7
7
  export const PROJECT_DISPLAY_NAME_GIT_CONFIG_KEY = "tutti.project-display-name";
8
+ export const PROJECT_DESCRIPTION_GIT_CONFIG_KEY = "tutti.project-description";
8
9
  function runGitRaw(args, cwd) {
9
10
  const result = spawnSync("git", [...args], {
10
11
  cwd,
@@ -76,6 +77,29 @@ export function readProjectDisplayName(workspaceRoot) {
76
77
  export function writeProjectDisplayName(workspaceRoot, displayName) {
77
78
  runGit(["config", "--local", PROJECT_DISPLAY_NAME_GIT_CONFIG_KEY, displayName], resolve(workspaceRoot));
78
79
  }
80
+ export function readProjectDescription(workspaceRoot) {
81
+ const result = runGitRaw(["config", "--local", "--get", PROJECT_DESCRIPTION_GIT_CONFIG_KEY], resolve(workspaceRoot));
82
+ if (result.status !== 0) {
83
+ return null;
84
+ }
85
+ const description = result.stdout.trim();
86
+ return description.length === 0 ? null : description;
87
+ }
88
+ export function writeProjectDescription(workspaceRoot, description) {
89
+ const workspace = resolve(workspaceRoot);
90
+ const trimmedDescription = description.trim();
91
+ if (trimmedDescription.length === 0) {
92
+ const result = runGitRaw(["config", "--local", "--unset", PROJECT_DESCRIPTION_GIT_CONFIG_KEY], workspace);
93
+ if (result.status === 0 || result.status === 5) {
94
+ return;
95
+ }
96
+ const stderr = result.stderr.trim();
97
+ throw new LaunchError("git_bootstrap_failed", stderr.length > 0
98
+ ? stderr
99
+ : `git config --local --unset ${PROJECT_DESCRIPTION_GIT_CONFIG_KEY} failed`, "Resolve the Git error and run `tutti launch` again.");
100
+ }
101
+ runGit(["config", "--local", PROJECT_DESCRIPTION_GIT_CONFIG_KEY, trimmedDescription], workspace);
102
+ }
79
103
  export function ensureProjectIdentity(workspaceRoot, projectId = createProjectId()) {
80
104
  const existing = readProjectIdentity(workspaceRoot);
81
105
  if (existing.kind === "present") {
@@ -31,4 +31,5 @@ export type AgentContextRouteOptions = Pick<HostProjectApiRouteOptions, "project
31
31
  export declare function requireProjectStore(options: AgentContextRouteOptions): HostProjectStore;
32
32
  export declare function requireWorkspaceRoot(options: AgentContextRouteOptions): string;
33
33
  export declare function requireTuttiHome(options: AgentContextRouteOptions): string;
34
+ export declare function requireProjectUserSkillsRoot(options: AgentContextRouteOptions): string;
34
35
  //# sourceMappingURL=agent-context-shared.d.ts.map
@@ -1,3 +1,4 @@
1
+ import { getProjectUserSkillsRoot } from "../../../skills/index.js";
1
2
  import { AgentContextBadRequestError } from "./agent-context-errors.js";
2
3
  export const AGENT_CONTEXT_BASE_PATH = "/internal/agent-context";
3
4
  export const MESSAGE_LIMIT_DEFAULT = 50;
@@ -41,4 +42,7 @@ export function requireTuttiHome(options) {
41
42
  }
42
43
  return options.tuttiHome;
43
44
  }
45
+ export function requireProjectUserSkillsRoot(options) {
46
+ return getProjectUserSkillsRoot(requireTuttiHome(options), options.project.project_id);
47
+ }
44
48
  //# sourceMappingURL=agent-context-shared.js.map
@@ -3,7 +3,7 @@ import { listUserSkills, readUserSkill, SkillServiceError, } from "../../../skil
3
3
  import { authorizeAgentContextRequest } from "./agent-context-auth.js";
4
4
  import { AgentContextBadRequestError } from "./agent-context-errors.js";
5
5
  import { boundedLimit, pageByOffset, queryString, truncateText } from "./agent-context-query.js";
6
- import { AGENT_CONTEXT_BASE_PATH, USER_SKILLS_LIMIT_DEFAULT, USER_SKILLS_LIMIT_MAX, USER_SKILL_MD_DEFAULT_MAX_CHARS, USER_SKILL_MD_MAX_CHARS, requireTuttiHome, } from "./agent-context-shared.js";
6
+ import { AGENT_CONTEXT_BASE_PATH, USER_SKILLS_LIMIT_DEFAULT, USER_SKILLS_LIMIT_MAX, USER_SKILL_MD_DEFAULT_MAX_CHARS, USER_SKILL_MD_MAX_CHARS, requireProjectUserSkillsRoot, } from "./agent-context-shared.js";
7
7
  function skillUnavailable(input) {
8
8
  return {
9
9
  kind: "unavailable",
@@ -37,7 +37,7 @@ export function registerAgentContextSkillRoutes(app, options) {
37
37
  max: USER_SKILLS_LIMIT_MAX,
38
38
  });
39
39
  const cursor = queryString(request.query.cursor);
40
- const page = pageByOffset(listUserSkills(requireTuttiHome(options)), {
40
+ const page = pageByOffset(listUserSkills(requireProjectUserSkillsRoot(options)), {
41
41
  endpoint: "skills",
42
42
  ...(cursor === undefined ? {} : { cursor }),
43
43
  limit,
@@ -59,7 +59,7 @@ export function registerAgentContextSkillRoutes(app, options) {
59
59
  });
60
60
  const skillName = request.params.skillName;
61
61
  try {
62
- const skill = readUserSkill(requireTuttiHome(options), skillName);
62
+ const skill = readUserSkill(requireProjectUserSkillsRoot(options), skillName);
63
63
  const skillMd = truncateText(redactText(skill.skill_md), maxChars);
64
64
  return {
65
65
  kind: "skill",
@@ -6,5 +6,6 @@ export declare function schedulerRunNowInvalidates(disposition: RunSchedulerNowD
6
6
  export declare function clarificationRoundInvalidates(roundId: ClarificationRoundRef): QueryInvalidationHint[];
7
7
  export declare function approvalInvalidates(): QueryInvalidationHint[];
8
8
  export declare function readStateInvalidates(): QueryInvalidationHint[];
9
+ export declare function projectMetadataInvalidates(): QueryInvalidationHint[];
9
10
  export declare function projectDisplayNameInvalidates(): QueryInvalidationHint[];
10
11
  //# sourceMappingURL=invalidations.d.ts.map
@@ -51,7 +51,10 @@ export function approvalInvalidates() {
51
51
  export function readStateInvalidates() {
52
52
  return [{ kind: "read_state" }, { kind: "bootstrap" }];
53
53
  }
54
- export function projectDisplayNameInvalidates() {
54
+ export function projectMetadataInvalidates() {
55
55
  return [{ kind: "workspace" }, { kind: "bootstrap" }];
56
56
  }
57
+ export function projectDisplayNameInvalidates() {
58
+ return projectMetadataInvalidates();
59
+ }
57
60
  //# sourceMappingURL=invalidations.js.map
@@ -88,7 +88,7 @@ export declare const HOST_PROJECT_COLLABORATION_OPENAPI_ROUTES: ({
88
88
  description: string;
89
89
  schema: import("@sinclair/typebox").TObject<{
90
90
  id: import("@sinclair/typebox").TLiteral<"current">;
91
- state: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"empty">, import("@sinclair/typebox").TLiteral<"ready">]>;
91
+ state: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"empty">, import("@sinclair/typebox").TLiteral<"ready">, import("@sinclair/typebox").TLiteral<"submitted">]>;
92
92
  topic: import("@sinclair/typebox").TString;
93
93
  background_summary: import("@sinclair/typebox").TString;
94
94
  current_consensus: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
@@ -96,6 +96,19 @@ export declare const HOST_PROJECT_COLLABORATION_OPENAPI_ROUTES: ({
96
96
  task_changes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
97
97
  updated_by_activity_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
98
98
  updated_at: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
99
+ submitted_by_activity_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
100
+ submitted_by_workflow_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
101
+ submitted_at: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
102
+ receipts: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
103
+ workflow_ref: import("@sinclair/typebox").TString;
104
+ activity_ref: import("@sinclair/typebox").TString;
105
+ topic: import("@sinclair/typebox").TString;
106
+ background_summary: import("@sinclair/typebox").TString;
107
+ current_consensus: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
108
+ open_questions: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
109
+ task_changes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
110
+ submitted_at: import("@sinclair/typebox").TString;
111
+ }>>;
99
112
  }>;
100
113
  contentType?: never;
101
114
  };
@@ -435,6 +435,10 @@ export declare const HOST_PROJECT_WORKSPACE_OPENAPI_ROUTES: ({
435
435
  readonly type: "string";
436
436
  readonly minLength: 1;
437
437
  };
438
+ readonly description: {
439
+ readonly type: "string";
440
+ readonly maxLength: 160;
441
+ };
438
442
  readonly identity_source: {
439
443
  readonly type: "string";
440
444
  readonly enum: readonly ["git_config"];
@@ -631,6 +635,10 @@ export declare const HOST_PROJECT_WORKSPACE_OPENAPI_ROUTES: ({
631
635
  readonly type: "string";
632
636
  readonly minLength: 1;
633
637
  };
638
+ readonly description: {
639
+ readonly type: "string";
640
+ readonly maxLength: 160;
641
+ };
634
642
  readonly identity_source: {
635
643
  readonly type: "string";
636
644
  readonly enum: readonly ["git_config"];
@@ -941,7 +949,7 @@ export declare const HOST_PROJECT_WORKSPACE_OPENAPI_ROUTES: ({
941
949
  readonly main_chat: import("@sinclair/typebox").TSchema;
942
950
  readonly scratchpad: import("@sinclair/typebox").TObject<{
943
951
  id: import("@sinclair/typebox").TLiteral<"current">;
944
- state: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"empty">, import("@sinclair/typebox").TLiteral<"ready">]>;
952
+ state: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"empty">, import("@sinclair/typebox").TLiteral<"ready">, import("@sinclair/typebox").TLiteral<"submitted">]>;
945
953
  topic: import("@sinclair/typebox").TString;
946
954
  background_summary: import("@sinclair/typebox").TString;
947
955
  current_consensus: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
@@ -949,6 +957,19 @@ export declare const HOST_PROJECT_WORKSPACE_OPENAPI_ROUTES: ({
949
957
  task_changes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
950
958
  updated_by_activity_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
951
959
  updated_at: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
960
+ submitted_by_activity_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
961
+ submitted_by_workflow_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
962
+ submitted_at: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
963
+ receipts: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
964
+ workflow_ref: import("@sinclair/typebox").TString;
965
+ activity_ref: import("@sinclair/typebox").TString;
966
+ topic: import("@sinclair/typebox").TString;
967
+ background_summary: import("@sinclair/typebox").TString;
968
+ current_consensus: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
969
+ open_questions: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
970
+ task_changes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
971
+ submitted_at: import("@sinclair/typebox").TString;
972
+ }>>;
952
973
  }>;
953
974
  readonly worklist: import("@sinclair/typebox").TObject<{
954
975
  modules: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
@@ -1,4 +1,4 @@
1
- import { MarkReadBodySchema, MarkReadResponseSchema, ReadStateSummaryProjectionSchema, UpdateProjectDisplayNameBodySchema, UpdateProjectDisplayNameResponseSchema, } from "@tutti/shared/schemas/api";
1
+ import { MarkReadBodySchema, MarkReadResponseSchema, ReadStateSummaryProjectionSchema, UpdateProjectDescriptionBodySchema, UpdateProjectDescriptionResponseSchema, UpdateProjectDisplayNameBodySchema, UpdateProjectDisplayNameResponseSchema, } from "@tutti/shared/schemas/api";
2
2
  import { HOST_PROJECT_API_BASE_PATH } from "./constants.js";
3
3
  import { BootstrapResponseSchema, ProviderConfigProjectionSchema, ProviderUsageProjectionSchema, SessionResponseSchema, WorkspaceResponseSchema, } from "./schemas.js";
4
4
  export const HOST_PROJECT_WORKSPACE_OPENAPI_ROUTES = [
@@ -57,6 +57,19 @@ export const HOST_PROJECT_WORKSPACE_OPENAPI_ROUTES = [
57
57
  },
58
58
  },
59
59
  },
60
+ {
61
+ method: "post",
62
+ path: `${HOST_PROJECT_API_BASE_PATH}/workspace/project-description`,
63
+ summary: "Update the current project description.",
64
+ tags: ["workspace"],
65
+ requestBody: UpdateProjectDescriptionBodySchema,
66
+ responses: {
67
+ 200: {
68
+ description: "Project description update command response.",
69
+ schema: UpdateProjectDescriptionResponseSchema,
70
+ },
71
+ },
72
+ },
60
73
  {
61
74
  method: "get",
62
75
  path: `${HOST_PROJECT_API_BASE_PATH}/bootstrap`,
@@ -250,7 +250,7 @@ export declare const HOST_PROJECT_OPENAPI_ROUTES: ({
250
250
  description: string;
251
251
  schema: import("@sinclair/typebox").TObject<{
252
252
  id: import("@sinclair/typebox").TLiteral<"current">;
253
- state: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"empty">, import("@sinclair/typebox").TLiteral<"ready">]>;
253
+ state: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"empty">, import("@sinclair/typebox").TLiteral<"ready">, import("@sinclair/typebox").TLiteral<"submitted">]>;
254
254
  topic: import("@sinclair/typebox").TString;
255
255
  background_summary: import("@sinclair/typebox").TString;
256
256
  current_consensus: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
@@ -258,6 +258,19 @@ export declare const HOST_PROJECT_OPENAPI_ROUTES: ({
258
258
  task_changes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
259
259
  updated_by_activity_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
260
260
  updated_at: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
261
+ submitted_by_activity_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
262
+ submitted_by_workflow_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
263
+ submitted_at: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
264
+ receipts: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
265
+ workflow_ref: import("@sinclair/typebox").TString;
266
+ activity_ref: import("@sinclair/typebox").TString;
267
+ topic: import("@sinclair/typebox").TString;
268
+ background_summary: import("@sinclair/typebox").TString;
269
+ current_consensus: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
270
+ open_questions: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
271
+ task_changes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
272
+ submitted_at: import("@sinclair/typebox").TString;
273
+ }>>;
261
274
  }>;
262
275
  contentType?: never;
263
276
  };
@@ -1600,6 +1613,10 @@ export declare const HOST_PROJECT_OPENAPI_ROUTES: ({
1600
1613
  readonly type: "string";
1601
1614
  readonly minLength: 1;
1602
1615
  };
1616
+ readonly description: {
1617
+ readonly type: "string";
1618
+ readonly maxLength: 160;
1619
+ };
1603
1620
  readonly identity_source: {
1604
1621
  readonly type: "string";
1605
1622
  readonly enum: readonly ["git_config"];
@@ -1784,6 +1801,10 @@ export declare const HOST_PROJECT_OPENAPI_ROUTES: ({
1784
1801
  readonly type: "string";
1785
1802
  readonly minLength: 1;
1786
1803
  };
1804
+ readonly description: {
1805
+ readonly type: "string";
1806
+ readonly maxLength: 160;
1807
+ };
1787
1808
  readonly identity_source: {
1788
1809
  readonly type: "string";
1789
1810
  readonly enum: readonly ["git_config"];
@@ -2094,7 +2115,7 @@ export declare const HOST_PROJECT_OPENAPI_ROUTES: ({
2094
2115
  readonly main_chat: import("@sinclair/typebox").TSchema;
2095
2116
  readonly scratchpad: import("@sinclair/typebox").TObject<{
2096
2117
  id: import("@sinclair/typebox").TLiteral<"current">;
2097
- state: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"empty">, import("@sinclair/typebox").TLiteral<"ready">]>;
2118
+ state: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"empty">, import("@sinclair/typebox").TLiteral<"ready">, import("@sinclair/typebox").TLiteral<"submitted">]>;
2098
2119
  topic: import("@sinclair/typebox").TString;
2099
2120
  background_summary: import("@sinclair/typebox").TString;
2100
2121
  current_consensus: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
@@ -2102,6 +2123,19 @@ export declare const HOST_PROJECT_OPENAPI_ROUTES: ({
2102
2123
  task_changes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
2103
2124
  updated_by_activity_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
2104
2125
  updated_at: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
2126
+ submitted_by_activity_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
2127
+ submitted_by_workflow_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
2128
+ submitted_at: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
2129
+ receipts: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
2130
+ workflow_ref: import("@sinclair/typebox").TString;
2131
+ activity_ref: import("@sinclair/typebox").TString;
2132
+ topic: import("@sinclair/typebox").TString;
2133
+ background_summary: import("@sinclair/typebox").TString;
2134
+ current_consensus: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
2135
+ open_questions: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
2136
+ task_changes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
2137
+ submitted_at: import("@sinclair/typebox").TString;
2138
+ }>>;
2105
2139
  }>;
2106
2140
  readonly worklist: import("@sinclair/typebox").TObject<{
2107
2141
  modules: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
@@ -1,6 +1,7 @@
1
- import type { GetMessagesRequest, SendClarificationRoundMessagePayload, SendMainChatMessagePayload, UpdateProjectDisplayNamePayload } from "@tutti/shared/schemas/api";
1
+ import type { GetMessagesRequest, SendClarificationRoundMessagePayload, SendMainChatMessagePayload, UpdateProjectDescriptionPayload, UpdateProjectDisplayNamePayload } from "@tutti/shared/schemas/api";
2
2
  export declare function requireValidMessageWindowRequest(query: GetMessagesRequest): GetMessagesRequest;
3
3
  export declare function requireValidSendMainChatPayload(payload: SendMainChatMessagePayload): SendMainChatMessagePayload;
4
4
  export declare function requireValidRoundMessagePayload(payload: SendClarificationRoundMessagePayload): SendClarificationRoundMessagePayload;
5
5
  export declare function requireValidProjectDisplayNamePayload(payload: UpdateProjectDisplayNamePayload): UpdateProjectDisplayNamePayload;
6
+ export declare function requireValidProjectDescriptionPayload(payload: UpdateProjectDescriptionPayload): UpdateProjectDescriptionPayload;
6
7
  //# sourceMappingURL=payload-validation.d.ts.map
@@ -33,4 +33,11 @@ export function requireValidProjectDisplayNamePayload(payload) {
33
33
  }
34
34
  return { display_name: displayName };
35
35
  }
36
+ export function requireValidProjectDescriptionPayload(payload) {
37
+ const description = payload.description.trim();
38
+ if (description.length > 160) {
39
+ throw new HostProjectValidationError("Project description cannot exceed 160 characters");
40
+ }
41
+ return { description };
42
+ }
36
43
  //# sourceMappingURL=payload-validation.js.map
@@ -440,6 +440,10 @@ export declare const WorkspaceResponseSchema: {
440
440
  readonly type: "string";
441
441
  readonly minLength: 1;
442
442
  };
443
+ readonly description: {
444
+ readonly type: "string";
445
+ readonly maxLength: 160;
446
+ };
443
447
  readonly identity_source: {
444
448
  readonly type: "string";
445
449
  readonly enum: readonly ["git_config"];
@@ -613,6 +617,10 @@ export declare const BootstrapResponseSchema: {
613
617
  readonly type: "string";
614
618
  readonly minLength: 1;
615
619
  };
620
+ readonly description: {
621
+ readonly type: "string";
622
+ readonly maxLength: 160;
623
+ };
616
624
  readonly identity_source: {
617
625
  readonly type: "string";
618
626
  readonly enum: readonly ["git_config"];
@@ -923,7 +931,7 @@ export declare const BootstrapResponseSchema: {
923
931
  readonly main_chat: import("@sinclair/typebox").TSchema;
924
932
  readonly scratchpad: import("@sinclair/typebox").TObject<{
925
933
  id: import("@sinclair/typebox").TLiteral<"current">;
926
- state: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"empty">, import("@sinclair/typebox").TLiteral<"ready">]>;
934
+ state: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"empty">, import("@sinclair/typebox").TLiteral<"ready">, import("@sinclair/typebox").TLiteral<"submitted">]>;
927
935
  topic: import("@sinclair/typebox").TString;
928
936
  background_summary: import("@sinclair/typebox").TString;
929
937
  current_consensus: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
@@ -931,6 +939,19 @@ export declare const BootstrapResponseSchema: {
931
939
  task_changes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
932
940
  updated_by_activity_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
933
941
  updated_at: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
942
+ submitted_by_activity_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
943
+ submitted_by_workflow_ref: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
944
+ submitted_at: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
945
+ receipts: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
946
+ workflow_ref: import("@sinclair/typebox").TString;
947
+ activity_ref: import("@sinclair/typebox").TString;
948
+ topic: import("@sinclair/typebox").TString;
949
+ background_summary: import("@sinclair/typebox").TString;
950
+ current_consensus: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
951
+ open_questions: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
952
+ task_changes: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>;
953
+ submitted_at: import("@sinclair/typebox").TString;
954
+ }>>;
934
955
  }>;
935
956
  readonly worklist: import("@sinclair/typebox").TObject<{
936
957
  modules: import("@sinclair/typebox").TArray<import("@sinclair/typebox").TObject<{
@@ -231,6 +231,7 @@ export const WorkspaceResponseSchema = {
231
231
  properties: {
232
232
  project_id: ProjectIdSchema,
233
233
  display_name: { type: "string", minLength: 1 },
234
+ description: { type: "string", maxLength: 160 },
234
235
  identity_source: { type: "string", enum: ["git_config"] },
235
236
  },
236
237
  },
@@ -2,6 +2,7 @@ import type { QueryInvalidationHint } from "@tutti/shared/schemas/api";
2
2
  import type { HostProjectApiRouteOptions } from "./types.js";
3
3
  export declare function skillsChangedInvalidates(): QueryInvalidationHint[];
4
4
  export declare function requireTuttiHome(options: HostProjectApiRouteOptions): string;
5
+ export declare function requireProjectUserSkillsRoot(options: HostProjectApiRouteOptions): string;
5
6
  export declare function withAsyncSkillServiceErrorMapping<T>(fn: () => Promise<T>): Promise<T>;
6
7
  export declare function withSkillServiceErrorMapping<T>(fn: () => T): T;
7
8
  //# sourceMappingURL=skills-helpers.d.ts.map
@@ -1,4 +1,4 @@
1
- import { SkillServiceError } from "../../../../skills/index.js";
1
+ import { getProjectUserSkillsRoot, SkillServiceError } from "../../../../skills/index.js";
2
2
  import { HostProjectBadRequestError, HostProjectNotFoundError, HostProjectStoreUnavailableError, HostProjectValidationError, } from "./errors.js";
3
3
  export function skillsChangedInvalidates() {
4
4
  return [{ kind: "skills" }, { kind: "read_state" }, { kind: "bootstrap" }];
@@ -9,6 +9,9 @@ export function requireTuttiHome(options) {
9
9
  }
10
10
  return options.tuttiHome;
11
11
  }
12
+ export function requireProjectUserSkillsRoot(options) {
13
+ return getProjectUserSkillsRoot(requireTuttiHome(options), options.project.project_id);
14
+ }
12
15
  function toHostProjectSkillError(error) {
13
16
  switch (error.code) {
14
17
  case "invalid_skill_name":
@@ -4,7 +4,7 @@ import { recordReadNotificationEvent } from "../../../../collaboration-state/ind
4
4
  import { decideCommandIdempotencyReplay, executeIdempotentCommand, readCommandIdempotencyRecord, } from "../../../command-idempotency/index.js";
5
5
  import { HOST_PROJECT_API_BASE_PATH } from "./constants.js";
6
6
  import { requireHostProjectSession, requireProjectStore } from "./session-requirements.js";
7
- import { requireTuttiHome, skillsChangedInvalidates, withAsyncSkillServiceErrorMapping, withSkillServiceErrorMapping, } from "./skills-helpers.js";
7
+ import { requireProjectUserSkillsRoot, skillsChangedInvalidates, withAsyncSkillServiceErrorMapping, withSkillServiceErrorMapping, } from "./skills-helpers.js";
8
8
  import { abortDownloadedStagedUpload, cleanupDownloadedStagedUpload, downloadStagedUpload, } from "./staged-uploads.js";
9
9
  export function registerSkillRoutes(app, { options, workspaceEvents, }) {
10
10
  app.get(`${HOST_PROJECT_API_BASE_PATH}/skills`, {
@@ -16,7 +16,7 @@ export function registerSkillRoutes(app, { options, workspaceEvents, }) {
16
16
  }, (request) => {
17
17
  requireHostProjectSession(request, options);
18
18
  return withSkillServiceErrorMapping(() => ({
19
- items: listUserSkills(requireTuttiHome(options)),
19
+ items: listUserSkills(requireProjectUserSkillsRoot(options)),
20
20
  }));
21
21
  });
22
22
  app.get(`${HOST_PROJECT_API_BASE_PATH}/skills/:skillName`, {
@@ -28,7 +28,7 @@ export function registerSkillRoutes(app, { options, workspaceEvents, }) {
28
28
  }, (request) => {
29
29
  requireHostProjectSession(request, options);
30
30
  return withSkillServiceErrorMapping(() => ({
31
- skill: readUserSkill(requireTuttiHome(options), request.params.skillName),
31
+ skill: readUserSkill(requireProjectUserSkillsRoot(options), request.params.skillName),
32
32
  }));
33
33
  });
34
34
  app.post(`${HOST_PROJECT_API_BASE_PATH}/skills/import`, {
@@ -78,7 +78,7 @@ export function registerSkillRoutes(app, { options, workspaceEvents, }) {
78
78
  throw new Error("Staged skill upload was not downloaded");
79
79
  }
80
80
  const imported = commitUserSkillImport({
81
- tuttiHome: requireTuttiHome(options),
81
+ userSkillsRoot: requireProjectUserSkillsRoot(options),
82
82
  plan,
83
83
  });
84
84
  recordReadNotificationEvent(tx, {
@@ -148,7 +148,7 @@ export function registerSkillRoutes(app, { options, workspaceEvents, }) {
148
148
  payload: request.body.payload,
149
149
  ...(options.now === undefined ? {} : { now: options.now }),
150
150
  execute: (tx) => withSkillServiceErrorMapping(() => {
151
- const disposition = deleteUserSkill(requireTuttiHome(options), request.body.payload.skill_name);
151
+ const disposition = deleteUserSkill(requireProjectUserSkillsRoot(options), request.body.payload.skill_name);
152
152
  if (disposition.kind === "deleted") {
153
153
  recordReadNotificationEvent(tx, {
154
154
  scope: "skills",
@@ -1,11 +1,11 @@
1
- import { UpdateProjectDisplayNameBodySchema, UpdateProjectDisplayNameResponseSchema, } from "@tutti/shared/schemas/api";
1
+ import { UpdateProjectDescriptionBodySchema, UpdateProjectDescriptionResponseSchema, UpdateProjectDisplayNameBodySchema, UpdateProjectDisplayNameResponseSchema, } from "@tutti/shared/schemas/api";
2
2
  import { BootstrapResponseSchema, GetEventsRequestSchema, OpenApiDocumentProjectionSchema, ProviderConfigProjectionSchema, ProviderUsageProjectionSchema, SessionResponseSchema, WorkspaceResponseSchema, } from "./schemas.js";
3
3
  import { executeIdempotentCommand } from "../../../command-idempotency/index.js";
4
4
  import { HOST_PROJECT_API_BASE_PATH } from "./constants.js";
5
5
  import { openWorkspaceEventStream } from "./events-stream.js";
6
- import { projectDisplayNameInvalidates } from "./invalidations.js";
6
+ import { projectMetadataInvalidates } from "./invalidations.js";
7
7
  import { buildHostProjectOpenApiDocument } from "./openapi.js";
8
- import { requireValidProjectDisplayNamePayload } from "./payload-validation.js";
8
+ import { requireValidProjectDescriptionPayload, requireValidProjectDisplayNamePayload, } from "./payload-validation.js";
9
9
  import { requireHostProjectSession, requireProjectStore, } from "./session-requirements.js";
10
10
  import { buildBootstrapSnapshots, resolveWorkspaceProjection, } from "./workspace-projections.js";
11
11
  function updateProjectDisplayName(options, payload) {
@@ -24,6 +24,27 @@ function updateProjectDisplayName(options, payload) {
24
24
  result: { project: { display_name: nextName } },
25
25
  };
26
26
  }
27
+ function updateProjectDescription(options, payload) {
28
+ const currentDescription = options.project.description ?? "";
29
+ const nextDescription = payload.description;
30
+ if (currentDescription === nextDescription) {
31
+ return {
32
+ disposition: { kind: "unchanged" },
33
+ result: { project: { description: currentDescription } },
34
+ };
35
+ }
36
+ options.updateProjectDescription?.(nextDescription);
37
+ if (nextDescription.length === 0) {
38
+ delete options.project.description;
39
+ }
40
+ else {
41
+ options.project.description = nextDescription;
42
+ }
43
+ return {
44
+ disposition: { kind: "updated" },
45
+ result: { project: { description: nextDescription } },
46
+ };
47
+ }
27
48
  export function registerSystemRoutes(app, { options, workspaceEvents, }) {
28
49
  app.get(`${HOST_PROJECT_API_BASE_PATH}/openapi.json`, {
29
50
  schema: {
@@ -97,7 +118,7 @@ export function registerSystemRoutes(app, { options, workspaceEvents, }) {
97
118
  ...(options.now === undefined ? {} : { now: options.now }),
98
119
  execute: () => updateProjectDisplayName(options, payload),
99
120
  });
100
- const invalidates = projectDisplayNameInvalidates();
121
+ const invalidates = projectMetadataInvalidates();
101
122
  const response = {
102
123
  command_id: execution.command_id,
103
124
  disposition: execution.disposition,
@@ -118,6 +139,47 @@ export function registerSystemRoutes(app, { options, workspaceEvents, }) {
118
139
  }
119
140
  return response;
120
141
  });
142
+ app.post(`${HOST_PROJECT_API_BASE_PATH}/workspace/project-description`, {
143
+ schema: {
144
+ body: UpdateProjectDescriptionBodySchema,
145
+ response: {
146
+ 200: UpdateProjectDescriptionResponseSchema,
147
+ },
148
+ },
149
+ }, (request) => {
150
+ requireHostProjectSession(request, options);
151
+ const store = requireProjectStore(options);
152
+ const payload = requireValidProjectDescriptionPayload(request.body.payload);
153
+ const execution = executeIdempotentCommand({
154
+ db: store.db,
155
+ command_id: request.body.command_id,
156
+ command_name: "workspace.update_project_description",
157
+ target_ref: `project:${options.project.project_id}`,
158
+ payload,
159
+ ...(options.now === undefined ? {} : { now: options.now }),
160
+ execute: () => updateProjectDescription(options, payload),
161
+ });
162
+ const invalidates = projectMetadataInvalidates();
163
+ const response = {
164
+ command_id: execution.command_id,
165
+ disposition: execution.disposition,
166
+ invalidates,
167
+ };
168
+ if (execution.result !== undefined) {
169
+ response.result = execution.result;
170
+ }
171
+ if (!execution.replayed) {
172
+ workspaceEvents.publish({
173
+ event_type: "query.invalidate",
174
+ payload: {
175
+ reason: "bulk_state_change",
176
+ message: "Project description changed.",
177
+ },
178
+ invalidates,
179
+ });
180
+ }
181
+ return response;
182
+ });
121
183
  app.get(`${HOST_PROJECT_API_BASE_PATH}/bootstrap`, {
122
184
  schema: {
123
185
  response: {
@@ -75,6 +75,7 @@ export type HostProjectApiRouteOptions = {
75
75
  project: {
76
76
  project_id: ProjectId;
77
77
  display_name: string;
78
+ description?: string;
78
79
  identity_source: "git_config";
79
80
  };
80
81
  server: {
@@ -98,6 +99,7 @@ export type HostProjectApiRouteOptions = {
98
99
  capabilities?: Partial<Omit<HostProjectApiCapabilities, "browser_secret_write">>;
99
100
  getRepo?: () => HostRepoProjection;
100
101
  updateProjectDisplayName?: (displayName: string) => void;
102
+ updateProjectDescription?: (description: string) => void;
101
103
  resolveStagedUpload?: (uploadId: RelayUploadProjection["upload_id"]) => Promise<ResolvedStagedUpload>;
102
104
  workspaceOps?: {
103
105
  workspaceRoot: string;
@@ -1,3 +1,4 @@
1
+ import type { ProjectId } from "@tutti/shared/ids";
1
2
  import type { SkillDetailProjection, SkillProjection } from "@tutti/shared/schemas/api";
2
3
  import { type UserSkillImportPlan } from "./zip-import.js";
3
4
  export { MAX_SKILL_DIRECTORY_DEPTH, MAX_SKILL_FILE_BYTES, MAX_SKILL_FILE_COUNT, MAX_SKILL_TOTAL_UNCOMPRESSED_BYTES, MAX_SKILL_ZIP_BYTES, SKILL_MD_FILE_NAME, USER_SKILLS_DIRECTORY_NAME, } from "./constants.js";
@@ -25,17 +26,17 @@ export type UserSkillImportResult = {
25
26
  disposition: UserSkillImportDisposition;
26
27
  skill: SkillDetailProjection;
27
28
  };
28
- export declare function getUserSkillsRoot(tuttiHome: string): string;
29
- export declare function listUserSkills(tuttiHome: string): SkillProjection[];
30
- export declare function readUserSkill(tuttiHome: string, skillName: string): SkillDetailProjection;
29
+ export declare function getProjectUserSkillsRoot(tuttiHome: string, projectId: ProjectId): string;
30
+ export declare function listUserSkills(userSkillsRoot: string): SkillProjection[];
31
+ export declare function readUserSkill(userSkillsRoot: string, skillName: string): SkillDetailProjection;
31
32
  export declare function importUserSkillZip(options: {
32
- tuttiHome: string;
33
+ userSkillsRoot: string;
33
34
  zipBuffer: Buffer;
34
35
  fileName: string;
35
36
  }): Promise<UserSkillImportResult>;
36
37
  export declare function commitUserSkillImport(options: {
37
- tuttiHome: string;
38
+ userSkillsRoot: string;
38
39
  plan: UserSkillImportPlan;
39
40
  }): UserSkillImportResult;
40
- export declare function deleteUserSkill(tuttiHome: string, skillName: string): UserSkillDeleteDisposition;
41
+ export declare function deleteUserSkill(userSkillsRoot: string, skillName: string): UserSkillDeleteDisposition;
41
42
  //# sourceMappingURL=index.d.ts.map