@codions/portta 0.0.0-develop.147.sha-1859ad1

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 (72) hide show
  1. package/dist/assets/skills/portta-workflows/SKILL.md +280 -0
  2. package/dist/assets/workflows/bake-off.workflow.js +171 -0
  3. package/dist/assets/workflows/code-review.workflow.js +179 -0
  4. package/dist/assets/workflows/deep-research.workflow.js +222 -0
  5. package/dist/assets/workflows/multi-provider-review.workflow.js +168 -0
  6. package/dist/assets/workflows/provider-debate.workflow.js +140 -0
  7. package/dist/assets/workflows/second-opinion.workflow.js +175 -0
  8. package/dist/cli.js +129793 -0
  9. package/dist/documentation.json +1 -0
  10. package/dist/host.js +73741 -0
  11. package/dist/runtime/.env.example +460 -0
  12. package/dist/runtime/VERSION +1 -0
  13. package/dist/runtime/config/traefik/dynamic/auth.example.yaml.disabled +24 -0
  14. package/dist/runtime/config/traefik/dynamic/middlewares.yaml +35 -0
  15. package/dist/runtime/config/traefik/dynamic/tcp.yaml +23 -0
  16. package/dist/runtime/docker/compose/attach/host.yaml +24 -0
  17. package/dist/runtime/docker/compose/attach/tailscale.yaml +70 -0
  18. package/dist/runtime/docker/compose/compose.yaml +214 -0
  19. package/dist/runtime/docker/compose/features/auth-build.yaml +19 -0
  20. package/dist/runtime/docker/compose/features/auth-dev.yaml +20 -0
  21. package/dist/runtime/docker/compose/features/cloudflare-tunnel.yaml +69 -0
  22. package/dist/runtime/docker/compose/features/dashboard-tailscale.yaml +21 -0
  23. package/dist/runtime/docker/compose/features/dashboard.yaml +21 -0
  24. package/dist/runtime/docker/compose/features/db.yaml +63 -0
  25. package/dist/runtime/docker/compose/features/panel-domain.yaml +42 -0
  26. package/dist/runtime/docker/compose/features/panel-public.yaml +47 -0
  27. package/dist/runtime/docker/compose/features/panel-webhook.yaml +46 -0
  28. package/dist/runtime/docker/compose/features/taskflow.yaml +38 -0
  29. package/dist/runtime/docker/compose/features/tcp-tailscale.yaml +30 -0
  30. package/dist/runtime/docker/compose/features/tcp.yaml +40 -0
  31. package/dist/runtime/docker/compose/features/web-bind.yaml +19 -0
  32. package/dist/runtime/docker/compose/features/web-build.yaml +21 -0
  33. package/dist/runtime/docker/compose/features/web-dev.yaml +54 -0
  34. package/dist/runtime/docker/compose/features/web-vpn.yaml +24 -0
  35. package/dist/runtime/docker/compose/features/web.yaml +283 -0
  36. package/dist/runtime/docker/compose/profiles/local-tls.yaml +23 -0
  37. package/dist/runtime/docker/compose/profiles/local.yaml +15 -0
  38. package/dist/runtime/docker/compose/profiles/public.yaml +19 -0
  39. package/dist/runtime/docker/compose/profiles/remote-tls-dns.yaml +26 -0
  40. package/dist/runtime/docker/compose/profiles/remote-tls-http.yaml +32 -0
  41. package/dist/runtime/docker/compose/profiles/remote-tls.yaml +31 -0
  42. package/dist/runtime/docker/compose/profiles/remote.yaml +23 -0
  43. package/dist/runtime/docker/images/README.md +12 -0
  44. package/dist/runtime/docker/images/apply/Dockerfile +45 -0
  45. package/dist/runtime/docker/images/sandbox/Dockerfile +79 -0
  46. package/dist/runtime/docker/images/sandbox/README.md +109 -0
  47. package/dist/runtime/docker/images/sandbox/VERSION +1 -0
  48. package/dist/runtime/docker/images/sandbox/agents/claude-code.sh +18 -0
  49. package/dist/runtime/docker/images/sandbox/agents/codex.sh +4 -0
  50. package/dist/runtime/docker/images/sandbox/agents/opencode.sh +4 -0
  51. package/dist/runtime/docker/images/sandbox/agents/pi.sh +4 -0
  52. package/dist/runtime/docker/images/sandbox/scripts/entrypoint.sh +8 -0
  53. package/dist/runtime/docker/images/sandbox/scripts/generate-manifest.mjs +81 -0
  54. package/dist/runtime/docker/images/sandbox/scripts/install-tools.sh +46 -0
  55. package/dist/runtime/docker/images/sandbox/scripts/verify-image.sh +18 -0
  56. package/dist/runtime/docker/images/toolbox/Dockerfile +38 -0
  57. package/dist/runtime/scripts/lib/runner-exec.sh +189 -0
  58. package/dist/runtime/templates/README.md +43 -0
  59. package/dist/runtime/templates/overlays/01-single-web.yaml +23 -0
  60. package/dist/runtime/templates/overlays/02-web-api.yaml +30 -0
  61. package/dist/runtime/templates/overlays/03-web-api-postgres.yaml +33 -0
  62. package/dist/runtime/templates/overlays/04-web-api-postgres-redis.yaml +30 -0
  63. package/dist/runtime/templates/overlays/05-multiple-apis.yaml +37 -0
  64. package/dist/runtime/templates/overlays/06-monorepo.yaml +63 -0
  65. package/dist/runtime/templates/overlays/07-worktree.env +47 -0
  66. package/dist/runtime/templates/overlays/08-nonstandard-port.yaml +41 -0
  67. package/dist/runtime/templates/overlays/09-tcp-routing.yaml +70 -0
  68. package/dist/runtime/templates/overlays/10-mailpit.yaml +24 -0
  69. package/dist/runtime/templates/overlays/11-rustfs.yaml +25 -0
  70. package/dist/runtime/templates/project/PORTTA.md +107 -0
  71. package/dist/supervisor.js +23928 -0
  72. package/package.json +63 -0
@@ -0,0 +1,175 @@
1
+ // taskflow-original built-in: ask both providers cheaply; if they agree, merge and return — if
2
+ // they disagree, escalate both to deep effort with the other's answer in hand, then adjudicate.
3
+ // Disagreement-triggered escalation: consensus-grade confidence at roughly half the cost of
4
+ // always running the full panel.
5
+ // Run with: portta flow workflows run second-opinion --args '"<question>"'
6
+
7
+ export const meta = {
8
+ name: "second-opinion",
9
+ description:
10
+ "Cheap consensus check — Codex and Claude answer at low effort; agreement returns a merged answer, disagreement escalates both to deep effort and adjudicates.",
11
+ defaultSandbox: "read-only",
12
+ workspace: {
13
+ default: "current_branch",
14
+ allowed: ["isolated_worktree", "new_branch", "current_branch"],
15
+ mutatesRepository: false,
16
+ reason: "The comparison is read-only and normally needs no Git isolation.",
17
+ },
18
+ phases: [
19
+ { title: "Ask", detail: "both providers answer independently at low effort" },
20
+ { title: "Compare", detail: "do the answers materially agree?" },
21
+ { title: "Escalate", detail: "deep reconsideration of the disagreements — only on a split" },
22
+ { title: "Answer", detail: "merge the consensus, or adjudicate the split" },
23
+ ],
24
+ }
25
+
26
+ const PROVIDERS = [
27
+ { provider: "codex", model: "gpt-5.5", name: "Codex" },
28
+ { provider: "claude-code", model: "claude-fable-5", name: "Claude" },
29
+ ]
30
+
31
+ const question =
32
+ typeof args === "string" && args.trim()
33
+ ? args.trim()
34
+ : args && typeof args === "object" && typeof args.question === "string" && args.question.trim()
35
+ ? args.question.trim()
36
+ : null
37
+ if (!question) {
38
+ throw new Error(`second-opinion needs a question — pass --args '"<question>"' or --args '{"question": "..."}'`)
39
+ }
40
+
41
+ const ANSWER_SCHEMA = {
42
+ type: "object",
43
+ required: ["answer", "keyPoints"],
44
+ properties: { answer: { type: "string" }, keyPoints: { type: "array", items: { type: "string" } } },
45
+ }
46
+
47
+ // ---------------------------------------------------------------------------
48
+ // Ask — identical prompts, low effort, blind to each other.
49
+ // ---------------------------------------------------------------------------
50
+ phase("Ask")
51
+ log(`asking both providers: ${question}`)
52
+
53
+ const asked = (
54
+ await parallel(
55
+ PROVIDERS.map((p) => () =>
56
+ agent(
57
+ `Answer this question concretely and commit to a position:\n\n${question}\n\n` +
58
+ `If it concerns the repository you are in, ground your answer in the actual code. Give ` +
59
+ `your answer plus the key points it rests on.`,
60
+ { provider: p.provider, model: p.model, effort: "low", label: `ask: ${p.name}`, phase: "Ask", schema: ANSWER_SCHEMA },
61
+ ).then((a) => ({ name: p.name, provider: p.provider, model: p.model, answer: a.answer, keyPoints: a.keyPoints })),
62
+ ),
63
+ )
64
+ ).filter(Boolean)
65
+
66
+ if (asked.length === 0) throw new Error("both providers failed to answer")
67
+ if (asked.length === 1) {
68
+ log(`warning: only ${asked[0].name} answered — no second opinion to compare`)
69
+ return { answer: asked[0].answer, agreed: null, escalated: false, answers: asked }
70
+ }
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // Compare — agreement on substance, not wording. A failed comparator counts
74
+ // as disagreement: escalating needlessly costs tokens, trusting a missing
75
+ // comparison costs correctness.
76
+ // ---------------------------------------------------------------------------
77
+ phase("Compare")
78
+
79
+ const cmp = await agent(
80
+ `Two independent answers to the same question:\n\nQuestion:\n${question}\n\n` +
81
+ `Answer 1 (${asked[0].name}):\n${asked[0].answer}\nKey points: ${asked[0].keyPoints.join("; ")}\n\n` +
82
+ `Answer 2 (${asked[1].name}):\n${asked[1].answer}\nKey points: ${asked[1].keyPoints.join("; ")}\n\n` +
83
+ `Do they reach the same substantive conclusion? Ignore wording, structure, and depth — flag ` +
84
+ `only material disagreements that would change what the asker does.`,
85
+ {
86
+ effort: "low",
87
+ label: "compare",
88
+ phase: "Compare",
89
+ schema: {
90
+ type: "object",
91
+ required: ["agree", "disagreements"],
92
+ properties: { agree: { type: "boolean" }, disagreements: { type: "array", items: { type: "string" } } },
93
+ },
94
+ },
95
+ )
96
+
97
+ const agree = cmp ? cmp.agree : false
98
+ const disagreements = cmp ? cmp.disagreements : ["comparison agent failed — treating the answers as unverified"]
99
+
100
+ if (agree) {
101
+ phase("Answer")
102
+ log("answers agree — merging")
103
+ const merged = await agent(
104
+ `Two independent analyses of this question reached the same conclusion. Merge them into one ` +
105
+ `answer — keep the strongest specifics of each, no filler.\n\nQuestion:\n${question}\n\n` +
106
+ `${asked.map((a) => `Answer (${a.name}):\n${a.answer}`).join("\n\n")}`,
107
+ { effort: "low", label: "merge", phase: "Answer" },
108
+ )
109
+ return { answer: merged, agreed: true, escalated: false, answers: asked }
110
+ }
111
+
112
+ // ---------------------------------------------------------------------------
113
+ // Escalate — each provider reconsiders at deep effort with the other's answer
114
+ // and the specific disagreements in hand.
115
+ // ---------------------------------------------------------------------------
116
+ phase("Escalate")
117
+ log(`answers disagree — escalating: ${disagreements.join("; ")}`)
118
+
119
+ const finals = (
120
+ await parallel(
121
+ asked.map((a, i) => () => {
122
+ const other = asked[1 - i]
123
+ return agent(
124
+ `You previously answered this question:\n\n${question}\n\nYour answer:\n${a.answer}\n\n` +
125
+ `An equally capable independent analysis disagreed. Its answer:\n${other.answer}\n\n` +
126
+ `The material disagreements:\n${disagreements.map((d) => `- ${d}`).join("\n")}\n\n` +
127
+ `Reconsider at full depth: verify the contested points yourself (read the code, check the ` +
128
+ `claims) rather than restating your position. Then either defend your answer with ` +
129
+ `evidence or change it. Changing your mind on the evidence is success, not failure. ` +
130
+ `State your final answer.`,
131
+ { provider: a.provider, model: a.model, effort: "xhigh", label: `reconsider: ${a.name}`, phase: "Escalate", schema: { type: "object", required: ["answer", "changed"], properties: { answer: { type: "string" }, changed: { type: "boolean" } } } },
132
+ ).then((f) => ({ name: a.name, answer: f.answer, changed: f.changed }))
133
+ }),
134
+ )
135
+ ).filter(Boolean)
136
+
137
+ // ---------------------------------------------------------------------------
138
+ // Answer — adjudicate what remains. Runs on the run-default provider
139
+ // (override with --provider).
140
+ // ---------------------------------------------------------------------------
141
+ phase("Answer")
142
+
143
+ const adj = await agent(
144
+ `You are the final adjudicator. Two analyses answered the same question, disagreed, and each ` +
145
+ `reconsidered at full depth with the other's answer in hand.\n\nQuestion:\n${question}\n\n` +
146
+ `Initial disagreements:\n${disagreements.map((d) => `- ${d}`).join("\n")}\n\n` +
147
+ `Final positions:\n${finals.map((f) => `${f.name}${f.changed ? " (revised)" : " (held)"}:\n${f.answer}`).join("\n\n")}\n\n` +
148
+ `Write the single answer the asker should act on. If the deep passes converged, merge them; if ` +
149
+ `not, decide on the merits — verify the decisive points yourself where feasible. Note anything ` +
150
+ `that remains genuinely unresolved.`,
151
+ {
152
+ effort: "xhigh",
153
+ label: "adjudicate",
154
+ phase: "Answer",
155
+ schema: {
156
+ type: "object",
157
+ required: ["answer", "resolution", "unresolved"],
158
+ properties: {
159
+ answer: { type: "string" },
160
+ resolution: { type: "string", enum: ["consensus", "adjudicated"] },
161
+ unresolved: { type: "array", items: { type: "string" } },
162
+ },
163
+ },
164
+ },
165
+ )
166
+
167
+ return {
168
+ answer: adj.answer,
169
+ agreed: false,
170
+ escalated: true,
171
+ resolution: adj.resolution,
172
+ unresolved: adj.unresolved,
173
+ disagreements,
174
+ positions: finals,
175
+ }